LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - formatting.c (source / functions) Coverage Total Hit UNC LBC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: ba12a202ce1b5581dc0ed149cf3f637d7897ad5d vs 2866d8c7dbfc9d882a7d80fef93fbbe763709932 Lines: 85.2 % 2331 1985 63 14 269 2 274 1709 112 370
Current Date: 2026-08-27 14:31:44 +0300 Functions: 93.2 % 73 68 5 16 52 8
Baseline: lcov-20260827-baseline Branches: 66.4 % 2399 1593 83 17 706 5 288 1300 5 5
Baseline Date: 2026-08-27 14:31:58 +0300 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(1,7] days: 80.0 % 340 272 61 7 269 3
(7,30] days: 0.0 % 43 0 43
(30,360] days: 91.3 % 103 94 9 94
(360..) days: 87.8 % 1845 1619 2 7 217 2 5 1612
Function coverage date bins:
(1,7] days: 100.0 % 8 8 8
(7,30] days: 0.0 % 1 0 1
(30,360] days: 92.9 % 14 13 1 1 12
(360..) days: 94.0 % 50 47 3 7 40
Branch coverage date bins:
(1,7] days: 76.8 % 375 288 83 4 288
(7,30] days: 0.0 % 24 0 24
(30,360] days: 61.9 % 118 73 45 73
(360..) days: 65.1 % 1892 1232 13 637 5 1227 5 5

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  * formatting.c
                                  3                 :                :  *
                                  4                 :                :  * src/backend/utils/adt/formatting.c
                                  5                 :                :  *
                                  6                 :                :  *
                                  7                 :                :  *   Portions Copyright (c) 1999-2026, PostgreSQL Global Development Group
                                  8                 :                :  *
                                  9                 :                :  *
                                 10                 :                :  *   TO_CHAR(); TO_TIMESTAMP(); TO_DATE(); TO_NUMBER();
                                 11                 :                :  *
                                 12                 :                :  *   The PostgreSQL routines for a timestamp/int/float/numeric formatting,
                                 13                 :                :  *   inspired by the Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines.
                                 14                 :                :  *
                                 15                 :                :  *
                                 16                 :                :  *   Cache & Memory:
                                 17                 :                :  *  Routines use (itself) internal cache for format pictures.
                                 18                 :                :  *
                                 19                 :                :  *  The cache uses a static buffer and is persistent across transactions.  If
                                 20                 :                :  *  the format-picture is bigger than the cache buffer, the parser is called
                                 21                 :                :  *  always.
                                 22                 :                :  *
                                 23                 :                :  *   NOTE for Number version:
                                 24                 :                :  *  All in this version is implemented as keywords ( => not used
                                 25                 :                :  *  suffixes), because a format picture is for *one* item (number)
                                 26                 :                :  *  only. It not is as a timestamp version, where each keyword (can)
                                 27                 :                :  *  has suffix.
                                 28                 :                :  *
                                 29                 :                :  *   NOTE for Timestamp routines:
                                 30                 :                :  *  In this module the POSIX 'struct tm' type is *not* used, but rather
                                 31                 :                :  *  PgSQL type, which has tm_mon based on one (*non* zero) and
                                 32                 :                :  *  year *not* based on 1900, but is used full year number.
                                 33                 :                :  *  Module supports AD / BC / AM / PM.
                                 34                 :                :  *
                                 35                 :                :  *  Supported types for to_char():
                                 36                 :                :  *
                                 37                 :                :  *      Timestamp, Numeric, int4, int8, float4, float8
                                 38                 :                :  *
                                 39                 :                :  *  Supported types for reverse conversion:
                                 40                 :                :  *
                                 41                 :                :  *      Timestamp   - to_timestamp()
                                 42                 :                :  *      Date        - to_date()
                                 43                 :                :  *      Numeric     - to_number()
                                 44                 :                :  *
                                 45                 :                :  *
                                 46                 :                :  *  Karel Zak
                                 47                 :                :  *
                                 48                 :                :  * TODO
                                 49                 :                :  *  - better number building (formatting) / parsing, now it isn't
                                 50                 :                :  *        ideal code
                                 51                 :                :  *  - use Assert()
                                 52                 :                :  *  - add support for number spelling
                                 53                 :                :  *  - add support for string to string formatting (we must be better
                                 54                 :                :  *    than Oracle :-),
                                 55                 :                :  *      to_char('Hello', 'X X X X X') -> 'H e l l o'
                                 56                 :                :  *
                                 57                 :                :  *-------------------------------------------------------------------------
                                 58                 :                :  */
                                 59                 :                : 
                                 60                 :                : #ifdef DEBUG_TO_FROM_CHAR
                                 61                 :                : #define DEBUG_elog_output   DEBUG3
                                 62                 :                : #endif
                                 63                 :                : 
                                 64                 :                : #include "postgres.h"
                                 65                 :                : 
                                 66                 :                : #include <ctype.h>
                                 67                 :                : #include <unistd.h>
                                 68                 :                : #include <math.h>
                                 69                 :                : #include <float.h>
                                 70                 :                : #include <limits.h>
                                 71                 :                : 
                                 72                 :                : #include "catalog/pg_type.h"
                                 73                 :                : #include "common/int.h"
                                 74                 :                : #include "mb/pg_wchar.h"
                                 75                 :                : #include "nodes/miscnodes.h"
                                 76                 :                : #include "parser/scansup.h"
                                 77                 :                : #include "utils/builtins.h"
                                 78                 :                : #include "utils/date.h"
                                 79                 :                : #include "utils/datetime.h"
                                 80                 :                : #include "utils/formatting.h"
                                 81                 :                : #include "utils/memutils.h"
                                 82                 :                : #include "utils/numeric.h"
                                 83                 :                : #include "utils/pg_locale.h"
                                 84                 :                : #include "varatt.h"
                                 85                 :                : 
                                 86                 :                : 
                                 87                 :                : /*
                                 88                 :                :  * Routines flags
                                 89                 :                :  */
                                 90                 :                : #define DCH_FLAG        0x1     /* DATE-TIME flag   */
                                 91                 :                : #define NUM_FLAG        0x2     /* NUMBER flag  */
                                 92                 :                : #define STD_FLAG        0x4     /* STANDARD flag    */
                                 93                 :                : 
                                 94                 :                : /*
                                 95                 :                :  * KeyWord Index (ascii from position 32 (' ') to 126 (~))
                                 96                 :                :  */
                                 97                 :                : #define KeyWord_INDEX_SIZE      ('~' - ' ')
                                 98                 :                : #define KeyWord_INDEX_FILTER(_c)    ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
                                 99                 :                : 
                                100                 :                : #define MAX_L10N_DATA           80  /* max localized day or month name */
                                101                 :                : 
                                102                 :                : /*
                                103                 :                :  * Format parser structs
                                104                 :                :  */
                                105                 :                : 
                                106                 :                : enum KeySuffixType
                                107                 :                : {
                                108                 :                :     SUFFTYPE_PREFIX = 1,
                                109                 :                :     SUFFTYPE_POSTFIX = 2,
                                110                 :                : };
                                111                 :                : 
                                112                 :                : typedef struct
                                113                 :                : {
                                114                 :                :     const char *name;           /* suffix string        */
                                115                 :                :     size_t      len;            /* suffix length        */
                                116                 :                :     int         id;             /* used in node->suffix */
                                117                 :                :     enum KeySuffixType type;    /* prefix / postfix     */
                                118                 :                : } KeySuffix;
                                119                 :                : 
                                120                 :                : /*
                                121                 :                :  * FromCharDateMode
                                122                 :                :  *
                                123                 :                :  * This value is used to nominate one of several distinct (and mutually
                                124                 :                :  * exclusive) date conventions that a keyword can belong to.
                                125                 :                :  */
                                126                 :                : typedef enum
                                127                 :                : {
                                128                 :                :     FROM_CHAR_DATE_NONE = 0,    /* Value does not affect date mode. */
                                129                 :                :     FROM_CHAR_DATE_GREGORIAN,   /* Gregorian (day, month, year) style date */
                                130                 :                :     FROM_CHAR_DATE_ISOWEEK,     /* ISO 8601 week date */
                                131                 :                : } FromCharDateMode;
                                132                 :                : 
                                133                 :                : typedef struct
                                134                 :                : {
                                135                 :                :     const char *name;
                                136                 :                :     size_t      len;
                                137                 :                :     int         id;
                                138                 :                :     bool        is_digit;
                                139                 :                :     FromCharDateMode date_mode;
                                140                 :                : } KeyWord;
                                141                 :                : 
                                142                 :                : enum FormatNodeType
                                143                 :                : {
                                144                 :                :     NODE_TYPE_END = 1,
                                145                 :                :     NODE_TYPE_ACTION = 2,
                                146                 :                :     NODE_TYPE_CHAR = 3,
                                147                 :                :     NODE_TYPE_SEPARATOR = 4,
                                148                 :                :     NODE_TYPE_SPACE = 5,
                                149                 :                : };
                                150                 :                : 
                                151                 :                : typedef struct
                                152                 :                : {
                                153                 :                :     enum FormatNodeType type;
                                154                 :                :     char        character[MAX_MULTIBYTE_CHAR_LEN + 1];  /* if type is CHAR */
                                155                 :                :     uint8       suffix;         /* keyword prefix/suffix code, if any
                                156                 :                :                                  * (DCH_SUFFIX_*) */
                                157                 :                :     const KeyWord *key;         /* if type is ACTION */
                                158                 :                : } FormatNode;
                                159                 :                : 
                                160                 :                : 
                                161                 :                : /*
                                162                 :                :  * Full months
                                163                 :                :  */
                                164                 :                : static const char *const months_full[] = {
                                165                 :                :     "January", "February", "March", "April", "May", "June", "July",
                                166                 :                :     "August", "September", "October", "November", "December", NULL
                                167                 :                : };
                                168                 :                : 
                                169                 :                : static const char *const days_short[] = {
                                170                 :                :     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
                                171                 :                : };
                                172                 :                : 
                                173                 :                : /*
                                174                 :                :  * AD / BC
                                175                 :                :  *
                                176                 :                :  *  There is no 0 AD.  Years go from 1 BC to 1 AD, so we make it
                                177                 :                :  *  positive and map year == -1 to year zero, and shift all negative
                                178                 :                :  *  years up one.  For interval years, we just return the year.
                                179                 :                :  */
                                180                 :                : #define ADJUST_YEAR(year, is_interval)  ((is_interval) ? (year) : ((year) <= 0 ? -((year) - 1) : (year)))
                                181                 :                : 
                                182                 :                : #define A_D_STR     "A.D."
                                183                 :                : #define a_d_STR     "a.d."
                                184                 :                : #define AD_STR      "AD"
                                185                 :                : #define ad_STR      "ad"
                                186                 :                : 
                                187                 :                : #define B_C_STR     "B.C."
                                188                 :                : #define b_c_STR     "b.c."
                                189                 :                : #define BC_STR      "BC"
                                190                 :                : #define bc_STR      "bc"
                                191                 :                : 
                                192                 :                : /*
                                193                 :                :  * AD / BC strings for seq_search.
                                194                 :                :  *
                                195                 :                :  * These are given in two variants, a long form with periods and a standard
                                196                 :                :  * form without.
                                197                 :                :  *
                                198                 :                :  * The array is laid out such that matches for AD have an even index, and
                                199                 :                :  * matches for BC have an odd index.  So the boolean value for BC is given by
                                200                 :                :  * taking the array index of the match, modulo 2.
                                201                 :                :  */
                                202                 :                : static const char *const adbc_strings[] = {ad_STR, bc_STR, AD_STR, BC_STR, NULL};
                                203                 :                : static const char *const adbc_strings_long[] = {a_d_STR, b_c_STR, A_D_STR, B_C_STR, NULL};
                                204                 :                : 
                                205                 :                : /*
                                206                 :                :  * AM / PM
                                207                 :                :  */
                                208                 :                : #define A_M_STR     "A.M."
                                209                 :                : #define a_m_STR     "a.m."
                                210                 :                : #define AM_STR      "AM"
                                211                 :                : #define am_STR      "am"
                                212                 :                : 
                                213                 :                : #define P_M_STR     "P.M."
                                214                 :                : #define p_m_STR     "p.m."
                                215                 :                : #define PM_STR      "PM"
                                216                 :                : #define pm_STR      "pm"
                                217                 :                : 
                                218                 :                : /*
                                219                 :                :  * AM / PM strings for seq_search.
                                220                 :                :  *
                                221                 :                :  * These are given in two variants, a long form with periods and a standard
                                222                 :                :  * form without.
                                223                 :                :  *
                                224                 :                :  * The array is laid out such that matches for AM have an even index, and
                                225                 :                :  * matches for PM have an odd index.  So the boolean value for PM is given by
                                226                 :                :  * taking the array index of the match, modulo 2.
                                227                 :                :  */
                                228                 :                : static const char *const ampm_strings[] = {am_STR, pm_STR, AM_STR, PM_STR, NULL};
                                229                 :                : static const char *const ampm_strings_long[] = {a_m_STR, p_m_STR, A_M_STR, P_M_STR, NULL};
                                230                 :                : 
                                231                 :                : /*
                                232                 :                :  * Months in roman-numeral
                                233                 :                :  * (Must be in reverse order for seq_search (in FROM_CHAR), because
                                234                 :                :  *  'VIII' must have higher precedence than 'V')
                                235                 :                :  */
                                236                 :                : static const char *const rm_months_upper[] =
                                237                 :                : {"XII", "XI", "X", "IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I", NULL};
                                238                 :                : 
                                239                 :                : static const char *const rm_months_lower[] =
                                240                 :                : {"xii", "xi", "x", "ix", "viii", "vii", "vi", "v", "iv", "iii", "ii", "i", NULL};
                                241                 :                : 
                                242                 :                : /*
                                243                 :                :  * Roman numerals
                                244                 :                :  */
                                245                 :                : static const char *const rm1[] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", NULL};
                                246                 :                : static const char *const rm10[] = {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", NULL};
                                247                 :                : static const char *const rm100[] = {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", NULL};
                                248                 :                : 
                                249                 :                : /*
                                250                 :                :  * MACRO: Check if the current and next characters form a valid subtraction
                                251                 :                :  * combination for roman numerals.
                                252                 :                :  */
                                253                 :                : #define IS_VALID_SUB_COMB(curr, next) \
                                254                 :                :     (((curr) == 'I' && ((next) == 'V' || (next) == 'X')) || \
                                255                 :                :      ((curr) == 'X' && ((next) == 'L' || (next) == 'C')) || \
                                256                 :                :      ((curr) == 'C' && ((next) == 'D' || (next) == 'M')))
                                257                 :                : 
                                258                 :                : /*
                                259                 :                :  * MACRO: Roman numeral value, or 0 if character isn't a roman numeral.
                                260                 :                :  */
                                261                 :                : #define ROMAN_VAL(r) \
                                262                 :                :     ((r) == 'I' ? 1 : \
                                263                 :                :      (r) == 'V' ? 5 : \
                                264                 :                :      (r) == 'X' ? 10 : \
                                265                 :                :      (r) == 'L' ? 50 : \
                                266                 :                :      (r) == 'C' ? 100 : \
                                267                 :                :      (r) == 'D' ? 500 : \
                                268                 :                :      (r) == 'M' ? 1000 : 0)
                                269                 :                : 
                                270                 :                : /*
                                271                 :                :  * 'MMMDCCCLXXXVIII' (3888) is the longest valid roman numeral (15 characters).
                                272                 :                :  */
                                273                 :                : #define MAX_ROMAN_LEN   15
                                274                 :                : 
                                275                 :                : /*
                                276                 :                :  * Ordinal postfixes
                                277                 :                :  */
                                278                 :                : static const char *const numTH[] = {"ST", "ND", "RD", "TH", NULL};
                                279                 :                : static const char *const numth[] = {"st", "nd", "rd", "th", NULL};
                                280                 :                : 
                                281                 :                : /*
                                282                 :                :  * Flags & Options:
                                283                 :                :  */
                                284                 :                : enum TH_Case
                                285                 :                : {
                                286                 :                :     TH_UPPER = 1,
                                287                 :                :     TH_LOWER = 2,
                                288                 :                : };
                                289                 :                : 
                                290                 :                : enum NUMDesc_lsign
                                291                 :                : {
                                292                 :                :     NUM_LSIGN_PRE = -1,
                                293                 :                :     NUM_LSIGN_POST = 1,
                                294                 :                :     NUM_LSIGN_NONE = 0,
                                295                 :                : };
                                296                 :                : 
                                297                 :                : /*
                                298                 :                :  * Number description struct
                                299                 :                :  */
                                300                 :                : typedef struct
                                301                 :                : {
                                302                 :                :     int         pre;            /* (count) numbers before decimal */
                                303                 :                :     int         post;           /* (count) numbers after decimal */
                                304                 :                :     enum NUMDesc_lsign lsign;   /* want locales sign */
                                305                 :                :     int         flag;           /* number parameters (NUM_F_*) */
                                306                 :                :     int         pre_lsign_num;  /* tmp value for lsign */
                                307                 :                :     int         multi;          /* multiplier for 'V' */
                                308                 :                :     int         zero_start;     /* position of first zero */
                                309                 :                :     int         zero_end;       /* position of last zero */
                                310                 :                :     bool        need_locale;    /* needs it locale */
                                311                 :                : } NUMDesc;
                                312                 :                : 
                                313                 :                : /*
                                314                 :                :  * Flags for NUMBER version
                                315                 :                :  */
                                316                 :                : #define NUM_F_DECIMAL       (1 << 1)
                                317                 :                : #define NUM_F_LDECIMAL      (1 << 2)
                                318                 :                : #define NUM_F_ZERO          (1 << 3)
                                319                 :                : #define NUM_F_BLANK         (1 << 4)
                                320                 :                : #define NUM_F_FILLMODE      (1 << 5)
                                321                 :                : #define NUM_F_LSIGN         (1 << 6)
                                322                 :                : #define NUM_F_BRACKET       (1 << 7)
                                323                 :                : #define NUM_F_MINUS         (1 << 8)
                                324                 :                : #define NUM_F_PLUS          (1 << 9)
                                325                 :                : #define NUM_F_ROMAN         (1 << 10)
                                326                 :                : #define NUM_F_MULTI         (1 << 11)
                                327                 :                : #define NUM_F_PLUS_POST     (1 << 12)
                                328                 :                : #define NUM_F_MINUS_POST    (1 << 13)
                                329                 :                : #define NUM_F_EEEE          (1 << 14)
                                330                 :                : 
                                331                 :                : /*
                                332                 :                :  * Tests
                                333                 :                :  */
                                334                 :                : #define IS_DECIMAL(_f)  ((_f)->flag & NUM_F_DECIMAL)
                                335                 :                : #define IS_LDECIMAL(_f) ((_f)->flag & NUM_F_LDECIMAL)
                                336                 :                : #define IS_ZERO(_f) ((_f)->flag & NUM_F_ZERO)
                                337                 :                : #define IS_BLANK(_f)    ((_f)->flag & NUM_F_BLANK)
                                338                 :                : #define IS_FILLMODE(_f) ((_f)->flag & NUM_F_FILLMODE)
                                339                 :                : #define IS_BRACKET(_f)  ((_f)->flag & NUM_F_BRACKET)
                                340                 :                : #define IS_MINUS(_f)    ((_f)->flag & NUM_F_MINUS)
                                341                 :                : #define IS_LSIGN(_f)    ((_f)->flag & NUM_F_LSIGN)
                                342                 :                : #define IS_PLUS(_f) ((_f)->flag & NUM_F_PLUS)
                                343                 :                : #define IS_ROMAN(_f)    ((_f)->flag & NUM_F_ROMAN)
                                344                 :                : #define IS_MULTI(_f)    ((_f)->flag & NUM_F_MULTI)
                                345                 :                : #define IS_EEEE(_f)     ((_f)->flag & NUM_F_EEEE)
                                346                 :                : 
                                347                 :                : /*
                                348                 :                :  * Format picture cache
                                349                 :                :  *
                                350                 :                :  * We will cache datetime format pictures up to DCH_CACHE_SIZE bytes long;
                                351                 :                :  * likewise number format pictures up to NUM_CACHE_SIZE bytes long.
                                352                 :                :  *
                                353                 :                :  * For simplicity, the cache entries are fixed-size, so they allow for the
                                354                 :                :  * worst case of a FormatNode for each byte in the picture string.
                                355                 :                :  *
                                356                 :                :  * The CACHE_SIZE constants are computed to make sizeof(DCHCacheEntry) and
                                357                 :                :  * sizeof(NUMCacheEntry) be powers of 2, or just less than that, so that
                                358                 :                :  * we don't waste too much space by palloc'ing them individually.  Be sure
                                359                 :                :  * to adjust those macros if you add fields to those structs.
                                360                 :                :  *
                                361                 :                :  * The max number of entries in each cache is DCH_CACHE_ENTRIES
                                362                 :                :  * resp. NUM_CACHE_ENTRIES.
                                363                 :                :  */
                                364                 :                : #define DCH_CACHE_OVERHEAD \
                                365                 :                :     MAXALIGN(sizeof(bool) + sizeof(int))
                                366                 :                : #define NUM_CACHE_OVERHEAD \
                                367                 :                :     MAXALIGN(sizeof(bool) + sizeof(int) + sizeof(NUMDesc))
                                368                 :                : 
                                369                 :                : #define DCH_CACHE_SIZE \
                                370                 :                :     ((2048 - DCH_CACHE_OVERHEAD) / (sizeof(FormatNode) + sizeof(char)) - 1)
                                371                 :                : #define NUM_CACHE_SIZE \
                                372                 :                :     ((1024 - NUM_CACHE_OVERHEAD) / (sizeof(FormatNode) + sizeof(char)) - 1)
                                373                 :                : 
                                374                 :                : #define DCH_CACHE_ENTRIES   20
                                375                 :                : #define NUM_CACHE_ENTRIES   20
                                376                 :                : 
                                377                 :                : typedef struct
                                378                 :                : {
                                379                 :                :     FormatNode  format[DCH_CACHE_SIZE + 1];
                                380                 :                :     char        str[DCH_CACHE_SIZE + 1];
                                381                 :                :     bool        std;
                                382                 :                :     bool        valid;
                                383                 :                :     int         age;
                                384                 :                : } DCHCacheEntry;
                                385                 :                : 
                                386                 :                : typedef struct
                                387                 :                : {
                                388                 :                :     FormatNode  format[NUM_CACHE_SIZE + 1];
                                389                 :                :     char        str[NUM_CACHE_SIZE + 1];
                                390                 :                :     bool        valid;
                                391                 :                :     int         age;
                                392                 :                :     NUMDesc     Num;
                                393                 :                : } NUMCacheEntry;
                                394                 :                : 
                                395                 :                : /* global cache for date/time format pictures */
                                396                 :                : static DCHCacheEntry *DCHCache[DCH_CACHE_ENTRIES];
                                397                 :                : static int  n_DCHCache = 0;     /* current number of entries */
                                398                 :                : static int  DCHCounter = 0;     /* aging-event counter */
                                399                 :                : 
                                400                 :                : /* global cache for number format pictures */
                                401                 :                : static NUMCacheEntry *NUMCache[NUM_CACHE_ENTRIES];
                                402                 :                : static int  n_NUMCache = 0;     /* current number of entries */
                                403                 :                : static int  NUMCounter = 0;     /* aging-event counter */
                                404                 :                : 
                                405                 :                : /*
                                406                 :                :  * For char->date/time conversion
                                407                 :                :  */
                                408                 :                : typedef struct
                                409                 :                : {
                                410                 :                :     FromCharDateMode mode;
                                411                 :                :     int         hh;
                                412                 :                :     int         pm;
                                413                 :                :     int         mi;
                                414                 :                :     int         ss;
                                415                 :                :     int         ssss;
                                416                 :                :     int         d;              /* stored as 1-7, Sunday = 1, 0 means missing */
                                417                 :                :     int         dd;
                                418                 :                :     int         ddd;
                                419                 :                :     int         mm;
                                420                 :                :     int         ms;
                                421                 :                :     int         year;
                                422                 :                :     int         bc;
                                423                 :                :     int         ww;
                                424                 :                :     int         w;
                                425                 :                :     int         cc;
                                426                 :                :     int         j;
                                427                 :                :     int         us;
                                428                 :                :     int         yysz;           /* is it YY or YYYY ? */
                                429                 :                :     bool        clock_12_hour;  /* 12 or 24 hour clock? */
                                430                 :                :     int         tzsign;         /* +1, -1, or 0 if no TZH/TZM fields */
                                431                 :                :     int         tzh;
                                432                 :                :     int         tzm;
                                433                 :                :     int         ff;             /* fractional precision */
                                434                 :                :     bool        has_tz;         /* was there a TZ field? */
                                435                 :                :     int         gmtoffset;      /* GMT offset of fixed-offset zone abbrev */
                                436                 :                :     pg_tz      *tzp;            /* pg_tz for dynamic abbrev */
                                437                 :                :     const char *abbrev;         /* dynamic abbrev */
                                438                 :                : } TmFromChar;
                                439                 :                : 
                                440                 :                : struct fmt_tz                   /* do_to_timestamp's timezone info output */
                                441                 :                : {
                                442                 :                :     bool        has_tz;         /* was there any TZ/TZH/TZM field? */
                                443                 :                :     int         gmtoffset;      /* GMT offset in seconds */
                                444                 :                : };
                                445                 :                : 
                                446                 :                : /*
                                447                 :                :  * Debug
                                448                 :                :  */
                                449                 :                : #ifdef DEBUG_TO_FROM_CHAR
                                450                 :                : #define DEBUG_TMFC(_X) \
                                451                 :                :         elog(DEBUG_elog_output, "TMFC:\nmode %d\nhh %d\npm %d\nmi %d\nss %d\nssss %d\nd %d\ndd %d\nddd %d\nmm %d\nms: %d\nyear %d\nbc %d\nww %d\nw %d\ncc %d\nj %d\nus: %d\nyysz: %d\nclock: %d", \
                                452                 :                :             (_X)->mode, (_X)->hh, (_X)->pm, (_X)->mi, (_X)->ss, (_X)->ssss, \
                                453                 :                :             (_X)->d, (_X)->dd, (_X)->ddd, (_X)->mm, (_X)->ms, (_X)->year, \
                                454                 :                :             (_X)->bc, (_X)->ww, (_X)->w, (_X)->cc, (_X)->j, (_X)->us, \
                                455                 :                :             (_X)->yysz, (_X)->clock_12_hour)
                                456                 :                : #define DEBUG_TM(_X) \
                                457                 :                :         elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon %d\n",\
                                458                 :                :             (_X)->tm_sec, (_X)->tm_year,\
                                459                 :                :             (_X)->tm_min, (_X)->tm_wday, (_X)->tm_hour, (_X)->tm_yday,\
                                460                 :                :             (_X)->tm_mday, (_X)->tm_isdst, (_X)->tm_mon)
                                461                 :                : #else
                                462                 :                : #define DEBUG_TMFC(_X)
                                463                 :                : #define DEBUG_TM(_X)
                                464                 :                : #endif
                                465                 :                : 
                                466                 :                : /*
                                467                 :                :  * Datetime to char conversion
                                468                 :                :  *
                                469                 :                :  * To support intervals as well as timestamps, we use a custom "tm" struct
                                470                 :                :  * that is almost like struct pg_tm, but has a 64-bit tm_hour field.
                                471                 :                :  * We omit the tm_isdst and tm_zone fields, which are not used here.
                                472                 :                :  */
                                473                 :                : struct fmt_tm
                                474                 :                : {
                                475                 :                :     int         tm_sec;
                                476                 :                :     int         tm_min;
                                477                 :                :     int64       tm_hour;
                                478                 :                :     int         tm_mday;
                                479                 :                :     int         tm_mon;
                                480                 :                :     int         tm_year;
                                481                 :                :     int         tm_wday;
                                482                 :                :     int         tm_yday;
                                483                 :                :     long int    tm_gmtoff;
                                484                 :                : };
                                485                 :                : 
                                486                 :                : typedef struct TmToChar
                                487                 :                : {
                                488                 :                :     struct fmt_tm tm;           /* almost the classic 'tm' struct */
                                489                 :                :     fsec_t      fsec;           /* fractional seconds */
                                490                 :                :     const char *tzn;            /* timezone */
                                491                 :                : } TmToChar;
                                492                 :                : 
                                493                 :                : #define tmtcTm(_X)  (&(_X)->tm)
                                494                 :                : #define tmtcTzn(_X) ((_X)->tzn)
                                495                 :                : #define tmtcFsec(_X)    ((_X)->fsec)
                                496                 :                : 
                                497                 :                : /* Note: this is used to copy pg_tm to fmt_tm, so not quite a bitwise copy */
                                498                 :                : #define COPY_tm(_DST, _SRC) \
                                499                 :                : do {    \
                                500                 :                :     (_DST)->tm_sec = (_SRC)->tm_sec; \
                                501                 :                :     (_DST)->tm_min = (_SRC)->tm_min; \
                                502                 :                :     (_DST)->tm_hour = (_SRC)->tm_hour; \
                                503                 :                :     (_DST)->tm_mday = (_SRC)->tm_mday; \
                                504                 :                :     (_DST)->tm_mon = (_SRC)->tm_mon; \
                                505                 :                :     (_DST)->tm_year = (_SRC)->tm_year; \
                                506                 :                :     (_DST)->tm_wday = (_SRC)->tm_wday; \
                                507                 :                :     (_DST)->tm_yday = (_SRC)->tm_yday; \
                                508                 :                :     (_DST)->tm_gmtoff = (_SRC)->tm_gmtoff; \
                                509                 :                : } while(0)
                                510                 :                : 
                                511                 :                : /* Caution: this is used to zero both pg_tm and fmt_tm structs */
                                512                 :                : #define ZERO_tm(_X) \
                                513                 :                : do {    \
                                514                 :                :     memset(_X, 0, sizeof(*(_X))); \
                                515                 :                :     (_X)->tm_mday = (_X)->tm_mon = 1; \
                                516                 :                : } while(0)
                                517                 :                : 
                                518                 :                : #define ZERO_tmtc(_X) \
                                519                 :                : do { \
                                520                 :                :     ZERO_tm( tmtcTm(_X) ); \
                                521                 :                :     tmtcFsec(_X) = 0; \
                                522                 :                :     tmtcTzn(_X) = NULL; \
                                523                 :                : } while(0)
                                524                 :                : 
                                525                 :                : /*
                                526                 :                :  *  to_char(time) appears to to_char() as an interval, so this check
                                527                 :                :  *  is really for interval and time data types.
                                528                 :                :  */
                                529                 :                : #define INVALID_FOR_INTERVAL  \
                                530                 :                : do { \
                                531                 :                :     if (is_interval) \
                                532                 :                :         ereport(ERROR, \
                                533                 :                :                 (errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
                                534                 :                :                  errmsg("invalid format specification for an interval value"), \
                                535                 :                :                  errhint("Intervals are not tied to specific calendar dates."))); \
                                536                 :                : } while(0)
                                537                 :                : 
                                538                 :                : /*****************************************************************************
                                539                 :                :  *          KeyWord definitions
                                540                 :                :  *****************************************************************************/
                                541                 :                : 
                                542                 :                : /*
                                543                 :                :  * Suffixes (FormatNode.suffix is an OR of these codes)
                                544                 :                :  */
                                545                 :                : #define DCH_SUFFIX_FM   0x01
                                546                 :                : #define DCH_SUFFIX_TH   0x02
                                547                 :                : #define DCH_SUFFIX_th   0x04
                                548                 :                : #define DCH_SUFFIX_SP   0x08
                                549                 :                : #define DCH_SUFFIX_TM   0x10
                                550                 :                : 
                                551                 :                : /*
                                552                 :                :  * Suffix tests
                                553                 :                :  */
                                554                 :                : static inline bool
  300 peter@eisentraut.org      555                 :CBC      196778 : IS_SUFFIX_TH(uint8 _s)
                                556                 :                : {
                                557                 :         196778 :     return (_s & DCH_SUFFIX_TH);
                                558                 :                : }
                                559                 :                : 
                                560                 :                : static inline bool
                                561                 :         196270 : IS_SUFFIX_th(uint8 _s)
                                562                 :                : {
                                563                 :         196270 :     return (_s & DCH_SUFFIX_th);
                                564                 :                : }
                                565                 :                : 
                                566                 :                : static inline bool
                                567                 :         196778 : IS_SUFFIX_THth(uint8 _s)
                                568                 :                : {
                                569   [ +  +  +  + ]:         196778 :     return IS_SUFFIX_TH(_s) || IS_SUFFIX_th(_s);
                                570                 :                : }
                                571                 :                : 
                                572                 :                : static inline enum TH_Case
                                573                 :           1524 : SUFFIX_TH_TYPE(uint8 _s)
                                574                 :                : {
                                575         [ +  + ]:           1524 :     return _s & DCH_SUFFIX_TH ? TH_UPPER : TH_LOWER;
                                576                 :                : }
                                577                 :                : 
                                578                 :                : /* Oracle toggles FM behavior, we don't; see docs. */
                                579                 :                : static inline bool
                                580                 :         116512 : IS_SUFFIX_FM(uint8 _s)
                                581                 :                : {
                                582                 :         116512 :     return (_s & DCH_SUFFIX_FM);
                                583                 :                : }
                                584                 :                : 
                                585                 :                : static inline bool
                                586                 :           9304 : IS_SUFFIX_TM(uint8 _s)
                                587                 :                : {
                                588                 :           9304 :     return (_s & DCH_SUFFIX_TM);
                                589                 :                : }
                                590                 :                : 
                                591                 :                : /*
                                592                 :                :  * Suffixes definition for DATE-TIME TO/FROM CHAR
                                593                 :                :  */
                                594                 :                : #define TM_SUFFIX_LEN   2
                                595                 :                : 
                                596                 :                : static const KeySuffix DCH_suff[] = {
                                597                 :                :     {"FM", 2, DCH_SUFFIX_FM, SUFFTYPE_PREFIX},
                                598                 :                :     {"fm", 2, DCH_SUFFIX_FM, SUFFTYPE_PREFIX},
                                599                 :                :     {"TM", TM_SUFFIX_LEN, DCH_SUFFIX_TM, SUFFTYPE_PREFIX},
                                600                 :                :     {"tm", 2, DCH_SUFFIX_TM, SUFFTYPE_PREFIX},
                                601                 :                :     {"TH", 2, DCH_SUFFIX_TH, SUFFTYPE_POSTFIX},
                                602                 :                :     {"th", 2, DCH_SUFFIX_th, SUFFTYPE_POSTFIX},
                                603                 :                :     {"SP", 2, DCH_SUFFIX_SP, SUFFTYPE_POSTFIX},
                                604                 :                :     /* last */
                                605                 :                :     {NULL, 0, 0, 0}
                                606                 :                : };
                                607                 :                : 
                                608                 :                : 
                                609                 :                : /*
                                610                 :                :  * Format-pictures (KeyWord).
                                611                 :                :  *
                                612                 :                :  * The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted
                                613                 :                :  *        complicated -to-> easy:
                                614                 :                :  *
                                615                 :                :  *  (example: "DDD","DD","Day","D" )
                                616                 :                :  *
                                617                 :                :  * (this specific sort needs the algorithm for sequential search for strings,
                                618                 :                :  * which not has exact end; -> How keyword is in "HH12blabla" ? - "HH"
                                619                 :                :  * or "HH12"? You must first try "HH12", because "HH" is in string, but
                                620                 :                :  * it is not good.
                                621                 :                :  *
                                622                 :                :  * (!)
                                623                 :                :  *   - Position for the keyword is similar as position in the enum DCH/NUM_poz.
                                624                 :                :  * (!)
                                625                 :                :  *
                                626                 :                :  * For fast search is used the 'int index[]', index is ascii table from position
                                627                 :                :  * 32 (' ') to 126 (~), in this index is DCH_ / NUM_ enums for each ASCII
                                628                 :                :  * position or -1 if char is not used in the KeyWord. Search example for
                                629                 :                :  * string "MM":
                                630                 :                :  *  1)  see in index to index['M' - 32],
                                631                 :                :  *  2)  take keywords position (enum DCH_MI) from index
                                632                 :                :  *  3)  run sequential search in keywords[] from this position
                                633                 :                :  */
                                634                 :                : 
                                635                 :                : typedef enum
                                636                 :                : {
                                637                 :                :     DCH_A_D,
                                638                 :                :     DCH_A_M,
                                639                 :                :     DCH_AD,
                                640                 :                :     DCH_AM,
                                641                 :                :     DCH_B_C,
                                642                 :                :     DCH_BC,
                                643                 :                :     DCH_CC,
                                644                 :                :     DCH_DAY,
                                645                 :                :     DCH_DDD,
                                646                 :                :     DCH_DD,
                                647                 :                :     DCH_DY,
                                648                 :                :     DCH_Day,
                                649                 :                :     DCH_Dy,
                                650                 :                :     DCH_D,
                                651                 :                :     DCH_FF1,                    /* FFn codes must be consecutive */
                                652                 :                :     DCH_FF2,
                                653                 :                :     DCH_FF3,
                                654                 :                :     DCH_FF4,
                                655                 :                :     DCH_FF5,
                                656                 :                :     DCH_FF6,
                                657                 :                :     DCH_FX,                     /* global suffix */
                                658                 :                :     DCH_HH24,
                                659                 :                :     DCH_HH12,
                                660                 :                :     DCH_HH,
                                661                 :                :     DCH_IDDD,
                                662                 :                :     DCH_ID,
                                663                 :                :     DCH_IW,
                                664                 :                :     DCH_IYYY,
                                665                 :                :     DCH_IYY,
                                666                 :                :     DCH_IY,
                                667                 :                :     DCH_I,
                                668                 :                :     DCH_J,
                                669                 :                :     DCH_MI,
                                670                 :                :     DCH_MM,
                                671                 :                :     DCH_MONTH,
                                672                 :                :     DCH_MON,
                                673                 :                :     DCH_MS,
                                674                 :                :     DCH_Month,
                                675                 :                :     DCH_Mon,
                                676                 :                :     DCH_OF,
                                677                 :                :     DCH_P_M,
                                678                 :                :     DCH_PM,
                                679                 :                :     DCH_Q,
                                680                 :                :     DCH_RM,
                                681                 :                :     DCH_SSSSS,
                                682                 :                :     DCH_SSSS,
                                683                 :                :     DCH_SS,
                                684                 :                :     DCH_TZH,
                                685                 :                :     DCH_TZM,
                                686                 :                :     DCH_TZ,
                                687                 :                :     DCH_US,
                                688                 :                :     DCH_WW,
                                689                 :                :     DCH_W,
                                690                 :                :     DCH_Y_YYY,
                                691                 :                :     DCH_YYYY,
                                692                 :                :     DCH_YYY,
                                693                 :                :     DCH_YY,
                                694                 :                :     DCH_Y,
                                695                 :                :     DCH_a_d,
                                696                 :                :     DCH_a_m,
                                697                 :                :     DCH_ad,
                                698                 :                :     DCH_am,
                                699                 :                :     DCH_b_c,
                                700                 :                :     DCH_bc,
                                701                 :                :     DCH_cc,
                                702                 :                :     DCH_day,
                                703                 :                :     DCH_ddd,
                                704                 :                :     DCH_dd,
                                705                 :                :     DCH_dy,
                                706                 :                :     DCH_d,
                                707                 :                :     DCH_ff1,
                                708                 :                :     DCH_ff2,
                                709                 :                :     DCH_ff3,
                                710                 :                :     DCH_ff4,
                                711                 :                :     DCH_ff5,
                                712                 :                :     DCH_ff6,
                                713                 :                :     DCH_fx,
                                714                 :                :     DCH_hh24,
                                715                 :                :     DCH_hh12,
                                716                 :                :     DCH_hh,
                                717                 :                :     DCH_iddd,
                                718                 :                :     DCH_id,
                                719                 :                :     DCH_iw,
                                720                 :                :     DCH_iyyy,
                                721                 :                :     DCH_iyy,
                                722                 :                :     DCH_iy,
                                723                 :                :     DCH_i,
                                724                 :                :     DCH_j,
                                725                 :                :     DCH_mi,
                                726                 :                :     DCH_mm,
                                727                 :                :     DCH_month,
                                728                 :                :     DCH_mon,
                                729                 :                :     DCH_ms,
                                730                 :                :     DCH_of,
                                731                 :                :     DCH_p_m,
                                732                 :                :     DCH_pm,
                                733                 :                :     DCH_q,
                                734                 :                :     DCH_rm,
                                735                 :                :     DCH_sssss,
                                736                 :                :     DCH_ssss,
                                737                 :                :     DCH_ss,
                                738                 :                :     DCH_tzh,
                                739                 :                :     DCH_tzm,
                                740                 :                :     DCH_tz,
                                741                 :                :     DCH_us,
                                742                 :                :     DCH_ww,
                                743                 :                :     DCH_w,
                                744                 :                :     DCH_y_yyy,
                                745                 :                :     DCH_yyyy,
                                746                 :                :     DCH_yyy,
                                747                 :                :     DCH_yy,
                                748                 :                :     DCH_y,
                                749                 :                : 
                                750                 :                :     /* last */
                                751                 :                :     _DCH_last_
                                752                 :                : }           DCH_poz;
                                753                 :                : 
                                754                 :                : typedef enum
                                755                 :                : {
                                756                 :                :     NUM_COMMA,
                                757                 :                :     NUM_DEC,
                                758                 :                :     NUM_0,
                                759                 :                :     NUM_9,
                                760                 :                :     NUM_B,
                                761                 :                :     NUM_C,
                                762                 :                :     NUM_D,
                                763                 :                :     NUM_E,
                                764                 :                :     NUM_FM,
                                765                 :                :     NUM_G,
                                766                 :                :     NUM_L,
                                767                 :                :     NUM_MI,
                                768                 :                :     NUM_PL,
                                769                 :                :     NUM_PR,
                                770                 :                :     NUM_RN,
                                771                 :                :     NUM_SG,
                                772                 :                :     NUM_SP,
                                773                 :                :     NUM_S,
                                774                 :                :     NUM_TH,
                                775                 :                :     NUM_V,
                                776                 :                :     NUM_b,
                                777                 :                :     NUM_c,
                                778                 :                :     NUM_d,
                                779                 :                :     NUM_e,
                                780                 :                :     NUM_fm,
                                781                 :                :     NUM_g,
                                782                 :                :     NUM_l,
                                783                 :                :     NUM_mi,
                                784                 :                :     NUM_pl,
                                785                 :                :     NUM_pr,
                                786                 :                :     NUM_rn,
                                787                 :                :     NUM_sg,
                                788                 :                :     NUM_sp,
                                789                 :                :     NUM_s,
                                790                 :                :     NUM_th,
                                791                 :                :     NUM_v,
                                792                 :                : 
                                793                 :                :     /* last */
                                794                 :                :     _NUM_last_
                                795                 :                : }           NUM_poz;
                                796                 :                : 
                                797                 :                : /*
                                798                 :                :  * KeyWords for DATE-TIME version
                                799                 :                :  */
                                800                 :                : static const KeyWord DCH_keywords[] = {
                                801                 :                : /*  name, len, id, is_digit, date_mode */
                                802                 :                :     {"A.D.", 4, DCH_A_D, false, FROM_CHAR_DATE_NONE}, /* A */
                                803                 :                :     {"A.M.", 4, DCH_A_M, false, FROM_CHAR_DATE_NONE},
                                804                 :                :     {"AD", 2, DCH_AD, false, FROM_CHAR_DATE_NONE},
                                805                 :                :     {"AM", 2, DCH_AM, false, FROM_CHAR_DATE_NONE},
                                806                 :                :     {"B.C.", 4, DCH_B_C, false, FROM_CHAR_DATE_NONE}, /* B */
                                807                 :                :     {"BC", 2, DCH_BC, false, FROM_CHAR_DATE_NONE},
                                808                 :                :     {"CC", 2, DCH_CC, true, FROM_CHAR_DATE_NONE}, /* C */
                                809                 :                :     {"DAY", 3, DCH_DAY, false, FROM_CHAR_DATE_NONE},  /* D */
                                810                 :                :     {"DDD", 3, DCH_DDD, true, FROM_CHAR_DATE_GREGORIAN},
                                811                 :                :     {"DD", 2, DCH_DD, true, FROM_CHAR_DATE_GREGORIAN},
                                812                 :                :     {"DY", 2, DCH_DY, false, FROM_CHAR_DATE_NONE},
                                813                 :                :     {"Day", 3, DCH_Day, false, FROM_CHAR_DATE_NONE},
                                814                 :                :     {"Dy", 2, DCH_Dy, false, FROM_CHAR_DATE_NONE},
                                815                 :                :     {"D", 1, DCH_D, true, FROM_CHAR_DATE_GREGORIAN},
                                816                 :                :     {"FF1", 3, DCH_FF1, true, FROM_CHAR_DATE_NONE}, /* F */
                                817                 :                :     {"FF2", 3, DCH_FF2, true, FROM_CHAR_DATE_NONE},
                                818                 :                :     {"FF3", 3, DCH_FF3, true, FROM_CHAR_DATE_NONE},
                                819                 :                :     {"FF4", 3, DCH_FF4, true, FROM_CHAR_DATE_NONE},
                                820                 :                :     {"FF5", 3, DCH_FF5, true, FROM_CHAR_DATE_NONE},
                                821                 :                :     {"FF6", 3, DCH_FF6, true, FROM_CHAR_DATE_NONE},
                                822                 :                :     {"FX", 2, DCH_FX, false, FROM_CHAR_DATE_NONE},
                                823                 :                :     {"HH24", 4, DCH_HH24, true, FROM_CHAR_DATE_NONE}, /* H */
                                824                 :                :     {"HH12", 4, DCH_HH12, true, FROM_CHAR_DATE_NONE},
                                825                 :                :     {"HH", 2, DCH_HH, true, FROM_CHAR_DATE_NONE},
                                826                 :                :     {"IDDD", 4, DCH_IDDD, true, FROM_CHAR_DATE_ISOWEEK},  /* I */
                                827                 :                :     {"ID", 2, DCH_ID, true, FROM_CHAR_DATE_ISOWEEK},
                                828                 :                :     {"IW", 2, DCH_IW, true, FROM_CHAR_DATE_ISOWEEK},
                                829                 :                :     {"IYYY", 4, DCH_IYYY, true, FROM_CHAR_DATE_ISOWEEK},
                                830                 :                :     {"IYY", 3, DCH_IYY, true, FROM_CHAR_DATE_ISOWEEK},
                                831                 :                :     {"IY", 2, DCH_IY, true, FROM_CHAR_DATE_ISOWEEK},
                                832                 :                :     {"I", 1, DCH_I, true, FROM_CHAR_DATE_ISOWEEK},
                                833                 :                :     {"J", 1, DCH_J, true, FROM_CHAR_DATE_NONE}, /* J */
                                834                 :                :     {"MI", 2, DCH_MI, true, FROM_CHAR_DATE_NONE}, /* M */
                                835                 :                :     {"MM", 2, DCH_MM, true, FROM_CHAR_DATE_GREGORIAN},
                                836                 :                :     {"MONTH", 5, DCH_MONTH, false, FROM_CHAR_DATE_GREGORIAN},
                                837                 :                :     {"MON", 3, DCH_MON, false, FROM_CHAR_DATE_GREGORIAN},
                                838                 :                :     {"MS", 2, DCH_MS, true, FROM_CHAR_DATE_NONE},
                                839                 :                :     {"Month", 5, DCH_Month, false, FROM_CHAR_DATE_GREGORIAN},
                                840                 :                :     {"Mon", 3, DCH_Mon, false, FROM_CHAR_DATE_GREGORIAN},
                                841                 :                :     {"OF", 2, DCH_OF, false, FROM_CHAR_DATE_NONE},    /* O */
                                842                 :                :     {"P.M.", 4, DCH_P_M, false, FROM_CHAR_DATE_NONE}, /* P */
                                843                 :                :     {"PM", 2, DCH_PM, false, FROM_CHAR_DATE_NONE},
                                844                 :                :     {"Q", 1, DCH_Q, true, FROM_CHAR_DATE_NONE}, /* Q */
                                845                 :                :     {"RM", 2, DCH_RM, false, FROM_CHAR_DATE_GREGORIAN}, /* R */
                                846                 :                :     {"SSSSS", 5, DCH_SSSS, true, FROM_CHAR_DATE_NONE},    /* S */
                                847                 :                :     {"SSSS", 4, DCH_SSSS, true, FROM_CHAR_DATE_NONE},
                                848                 :                :     {"SS", 2, DCH_SS, true, FROM_CHAR_DATE_NONE},
                                849                 :                :     {"TZH", 3, DCH_TZH, false, FROM_CHAR_DATE_NONE},  /* T */
                                850                 :                :     {"TZM", 3, DCH_TZM, true, FROM_CHAR_DATE_NONE},
                                851                 :                :     {"TZ", 2, DCH_TZ, false, FROM_CHAR_DATE_NONE},
                                852                 :                :     {"US", 2, DCH_US, true, FROM_CHAR_DATE_NONE}, /* U */
                                853                 :                :     {"WW", 2, DCH_WW, true, FROM_CHAR_DATE_GREGORIAN},    /* W */
                                854                 :                :     {"W", 1, DCH_W, true, FROM_CHAR_DATE_GREGORIAN},
                                855                 :                :     {"Y,YYY", 5, DCH_Y_YYY, true, FROM_CHAR_DATE_GREGORIAN},  /* Y */
                                856                 :                :     {"YYYY", 4, DCH_YYYY, true, FROM_CHAR_DATE_GREGORIAN},
                                857                 :                :     {"YYY", 3, DCH_YYY, true, FROM_CHAR_DATE_GREGORIAN},
                                858                 :                :     {"YY", 2, DCH_YY, true, FROM_CHAR_DATE_GREGORIAN},
                                859                 :                :     {"Y", 1, DCH_Y, true, FROM_CHAR_DATE_GREGORIAN},
                                860                 :                :     {"a.d.", 4, DCH_a_d, false, FROM_CHAR_DATE_NONE}, /* a */
                                861                 :                :     {"a.m.", 4, DCH_a_m, false, FROM_CHAR_DATE_NONE},
                                862                 :                :     {"ad", 2, DCH_ad, false, FROM_CHAR_DATE_NONE},
                                863                 :                :     {"am", 2, DCH_am, false, FROM_CHAR_DATE_NONE},
                                864                 :                :     {"b.c.", 4, DCH_b_c, false, FROM_CHAR_DATE_NONE}, /* b */
                                865                 :                :     {"bc", 2, DCH_bc, false, FROM_CHAR_DATE_NONE},
                                866                 :                :     {"cc", 2, DCH_CC, true, FROM_CHAR_DATE_NONE}, /* c */
                                867                 :                :     {"day", 3, DCH_day, false, FROM_CHAR_DATE_NONE},  /* d */
                                868                 :                :     {"ddd", 3, DCH_DDD, true, FROM_CHAR_DATE_GREGORIAN},
                                869                 :                :     {"dd", 2, DCH_DD, true, FROM_CHAR_DATE_GREGORIAN},
                                870                 :                :     {"dy", 2, DCH_dy, false, FROM_CHAR_DATE_NONE},
                                871                 :                :     {"d", 1, DCH_D, true, FROM_CHAR_DATE_GREGORIAN},
                                872                 :                :     {"ff1", 3, DCH_FF1, true, FROM_CHAR_DATE_NONE}, /* f */
                                873                 :                :     {"ff2", 3, DCH_FF2, true, FROM_CHAR_DATE_NONE},
                                874                 :                :     {"ff3", 3, DCH_FF3, true, FROM_CHAR_DATE_NONE},
                                875                 :                :     {"ff4", 3, DCH_FF4, true, FROM_CHAR_DATE_NONE},
                                876                 :                :     {"ff5", 3, DCH_FF5, true, FROM_CHAR_DATE_NONE},
                                877                 :                :     {"ff6", 3, DCH_FF6, true, FROM_CHAR_DATE_NONE},
                                878                 :                :     {"fx", 2, DCH_FX, false, FROM_CHAR_DATE_NONE},
                                879                 :                :     {"hh24", 4, DCH_HH24, true, FROM_CHAR_DATE_NONE}, /* h */
                                880                 :                :     {"hh12", 4, DCH_HH12, true, FROM_CHAR_DATE_NONE},
                                881                 :                :     {"hh", 2, DCH_HH, true, FROM_CHAR_DATE_NONE},
                                882                 :                :     {"iddd", 4, DCH_IDDD, true, FROM_CHAR_DATE_ISOWEEK},  /* i */
                                883                 :                :     {"id", 2, DCH_ID, true, FROM_CHAR_DATE_ISOWEEK},
                                884                 :                :     {"iw", 2, DCH_IW, true, FROM_CHAR_DATE_ISOWEEK},
                                885                 :                :     {"iyyy", 4, DCH_IYYY, true, FROM_CHAR_DATE_ISOWEEK},
                                886                 :                :     {"iyy", 3, DCH_IYY, true, FROM_CHAR_DATE_ISOWEEK},
                                887                 :                :     {"iy", 2, DCH_IY, true, FROM_CHAR_DATE_ISOWEEK},
                                888                 :                :     {"i", 1, DCH_I, true, FROM_CHAR_DATE_ISOWEEK},
                                889                 :                :     {"j", 1, DCH_J, true, FROM_CHAR_DATE_NONE}, /* j */
                                890                 :                :     {"mi", 2, DCH_MI, true, FROM_CHAR_DATE_NONE}, /* m */
                                891                 :                :     {"mm", 2, DCH_MM, true, FROM_CHAR_DATE_GREGORIAN},
                                892                 :                :     {"month", 5, DCH_month, false, FROM_CHAR_DATE_GREGORIAN},
                                893                 :                :     {"mon", 3, DCH_mon, false, FROM_CHAR_DATE_GREGORIAN},
                                894                 :                :     {"ms", 2, DCH_MS, true, FROM_CHAR_DATE_NONE},
                                895                 :                :     {"of", 2, DCH_OF, false, FROM_CHAR_DATE_NONE},    /* o */
                                896                 :                :     {"p.m.", 4, DCH_p_m, false, FROM_CHAR_DATE_NONE}, /* p */
                                897                 :                :     {"pm", 2, DCH_pm, false, FROM_CHAR_DATE_NONE},
                                898                 :                :     {"q", 1, DCH_Q, true, FROM_CHAR_DATE_NONE}, /* q */
                                899                 :                :     {"rm", 2, DCH_rm, false, FROM_CHAR_DATE_GREGORIAN}, /* r */
                                900                 :                :     {"sssss", 5, DCH_SSSS, true, FROM_CHAR_DATE_NONE},    /* s */
                                901                 :                :     {"ssss", 4, DCH_SSSS, true, FROM_CHAR_DATE_NONE},
                                902                 :                :     {"ss", 2, DCH_SS, true, FROM_CHAR_DATE_NONE},
                                903                 :                :     {"tzh", 3, DCH_TZH, false, FROM_CHAR_DATE_NONE},  /* t */
                                904                 :                :     {"tzm", 3, DCH_TZM, true, FROM_CHAR_DATE_NONE},
                                905                 :                :     {"tz", 2, DCH_tz, false, FROM_CHAR_DATE_NONE},
                                906                 :                :     {"us", 2, DCH_US, true, FROM_CHAR_DATE_NONE}, /* u */
                                907                 :                :     {"ww", 2, DCH_WW, true, FROM_CHAR_DATE_GREGORIAN},    /* w */
                                908                 :                :     {"w", 1, DCH_W, true, FROM_CHAR_DATE_GREGORIAN},
                                909                 :                :     {"y,yyy", 5, DCH_Y_YYY, true, FROM_CHAR_DATE_GREGORIAN},  /* y */
                                910                 :                :     {"yyyy", 4, DCH_YYYY, true, FROM_CHAR_DATE_GREGORIAN},
                                911                 :                :     {"yyy", 3, DCH_YYY, true, FROM_CHAR_DATE_GREGORIAN},
                                912                 :                :     {"yy", 2, DCH_YY, true, FROM_CHAR_DATE_GREGORIAN},
                                913                 :                :     {"y", 1, DCH_Y, true, FROM_CHAR_DATE_GREGORIAN},
                                914                 :                : 
                                915                 :                :     /* last */
                                916                 :                :     {NULL, 0, 0, 0, 0}
                                917                 :                : };
                                918                 :                : 
                                919                 :                : /*
                                920                 :                :  * KeyWords for NUMBER version
                                921                 :                :  *
                                922                 :                :  * The is_digit and date_mode fields are not relevant here.
                                923                 :                :  */
                                924                 :                : static const KeyWord NUM_keywords[] = {
                                925                 :                : /*  name, len, id           is in Index */
                                926                 :                :     {",", 1, NUM_COMMA},      /* , */
                                927                 :                :     {".", 1, NUM_DEC},            /* . */
                                928                 :                :     {"0", 1, NUM_0},          /* 0 */
                                929                 :                :     {"9", 1, NUM_9},          /* 9 */
                                930                 :                :     {"B", 1, NUM_B},          /* B */
                                931                 :                :     {"C", 1, NUM_C},          /* C */
                                932                 :                :     {"D", 1, NUM_D},          /* D */
                                933                 :                :     {"EEEE", 4, NUM_E},           /* E */
                                934                 :                :     {"FM", 2, NUM_FM},            /* F */
                                935                 :                :     {"G", 1, NUM_G},          /* G */
                                936                 :                :     {"L", 1, NUM_L},          /* L */
                                937                 :                :     {"MI", 2, NUM_MI},            /* M */
                                938                 :                :     {"PL", 2, NUM_PL},            /* P */
                                939                 :                :     {"PR", 2, NUM_PR},
                                940                 :                :     {"RN", 2, NUM_RN},            /* R */
                                941                 :                :     {"SG", 2, NUM_SG},            /* S */
                                942                 :                :     {"SP", 2, NUM_SP},
                                943                 :                :     {"S", 1, NUM_S},
                                944                 :                :     {"TH", 2, NUM_TH},            /* T */
                                945                 :                :     {"V", 1, NUM_V},          /* V */
                                946                 :                :     {"b", 1, NUM_B},          /* b */
                                947                 :                :     {"c", 1, NUM_C},          /* c */
                                948                 :                :     {"d", 1, NUM_D},          /* d */
                                949                 :                :     {"eeee", 4, NUM_E},           /* e */
                                950                 :                :     {"fm", 2, NUM_FM},            /* f */
                                951                 :                :     {"g", 1, NUM_G},          /* g */
                                952                 :                :     {"l", 1, NUM_L},          /* l */
                                953                 :                :     {"mi", 2, NUM_MI},            /* m */
                                954                 :                :     {"pl", 2, NUM_PL},            /* p */
                                955                 :                :     {"pr", 2, NUM_PR},
                                956                 :                :     {"rn", 2, NUM_rn},            /* r */
                                957                 :                :     {"sg", 2, NUM_SG},            /* s */
                                958                 :                :     {"sp", 2, NUM_SP},
                                959                 :                :     {"s", 1, NUM_S},
                                960                 :                :     {"th", 2, NUM_th},            /* t */
                                961                 :                :     {"v", 1, NUM_V},          /* v */
                                962                 :                : 
                                963                 :                :     /* last */
                                964                 :                :     {NULL, 0, 0}
                                965                 :                : };
                                966                 :                : 
                                967                 :                : 
                                968                 :                : /*
                                969                 :                :  * KeyWords index for DATE-TIME version
                                970                 :                :  */
                                971                 :                : static const int DCH_index[KeyWord_INDEX_SIZE] = {
                                972                 :                : /*
                                973                 :                :  * 0    1   2   3   4   5   6   7   8   9
                                974                 :                :  */
                                975                 :                :     /*---- first 0..31 chars are skipped ----*/
                                976                 :                : 
                                977                 :                :     -1, -1, -1, -1, -1, -1, -1, -1,
                                978                 :                :     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                                979                 :                :     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                                980                 :                :     -1, -1, -1, -1, -1, DCH_A_D, DCH_B_C, DCH_CC, DCH_DAY, -1,
                                981                 :                :     DCH_FF1, -1, DCH_HH24, DCH_IDDD, DCH_J, -1, -1, DCH_MI, -1, DCH_OF,
                                982                 :                :     DCH_P_M, DCH_Q, DCH_RM, DCH_SSSSS, DCH_TZH, DCH_US, -1, DCH_WW, -1, DCH_Y_YYY,
                                983                 :                :     -1, -1, -1, -1, -1, -1, -1, DCH_a_d, DCH_b_c, DCH_cc,
                                984                 :                :     DCH_day, -1, DCH_ff1, -1, DCH_hh24, DCH_iddd, DCH_j, -1, -1, DCH_mi,
                                985                 :                :     -1, DCH_of, DCH_p_m, DCH_q, DCH_rm, DCH_sssss, DCH_tzh, DCH_us, -1, DCH_ww,
                                986                 :                :     -1, DCH_y_yyy, -1, -1, -1, -1
                                987                 :                : 
                                988                 :                :     /*---- chars over 126 are skipped ----*/
                                989                 :                : };
                                990                 :                : 
                                991                 :                : /*
                                992                 :                :  * KeyWords index for NUMBER version
                                993                 :                :  */
                                994                 :                : static const int NUM_index[KeyWord_INDEX_SIZE] = {
                                995                 :                : /*
                                996                 :                :  * 0    1   2   3   4   5   6   7   8   9
                                997                 :                :  */
                                998                 :                :     /*---- first 0..31 chars are skipped ----*/
                                999                 :                : 
                               1000                 :                :     -1, -1, -1, -1, -1, -1, -1, -1,
                               1001                 :                :     -1, -1, -1, -1, NUM_COMMA, -1, NUM_DEC, -1, NUM_0, -1,
                               1002                 :                :     -1, -1, -1, -1, -1, -1, -1, NUM_9, -1, -1,
                               1003                 :                :     -1, -1, -1, -1, -1, -1, NUM_B, NUM_C, NUM_D, NUM_E,
                               1004                 :                :     NUM_FM, NUM_G, -1, -1, -1, -1, NUM_L, NUM_MI, -1, -1,
                               1005                 :                :     NUM_PL, -1, NUM_RN, NUM_SG, NUM_TH, -1, NUM_V, -1, -1, -1,
                               1006                 :                :     -1, -1, -1, -1, -1, -1, -1, -1, NUM_b, NUM_c,
                               1007                 :                :     NUM_d, NUM_e, NUM_fm, NUM_g, -1, -1, -1, -1, NUM_l, NUM_mi,
                               1008                 :                :     -1, -1, NUM_pl, -1, NUM_rn, NUM_sg, NUM_th, -1, NUM_v, -1,
                               1009                 :                :     -1, -1, -1, -1, -1, -1
                               1010                 :                : 
                               1011                 :                :     /*---- chars over 126 are skipped ----*/
                               1012                 :                : };
                               1013                 :                : 
                               1014                 :                : /*
                               1015                 :                :  * Number processor struct
                               1016                 :                :  */
                               1017                 :                : typedef struct NUMProc
                               1018                 :                : {
                               1019                 :                :     NUMDesc    *Num;            /* number description       */
                               1020                 :                : 
                               1021                 :                :     int         sign,           /* '-' or '+'           */
                               1022                 :                :                 sign_wrote,     /* was sign write       */
                               1023                 :                :                 num_count,      /* number of write digits   */
                               1024                 :                :                 num_in,         /* is inside number     */
                               1025                 :                :                 num_curr,       /* current position in number   */
                               1026                 :                :                 out_pre_spaces, /* to_char: spaces needed before first digit */
                               1027                 :                : 
                               1028                 :                :                 read_dec,       /* to_number - was read dec. point  */
                               1029                 :                :                 read_post,      /* to_number - number of dec. digit */
                               1030                 :                :                 read_pre;       /* to_number - number non-dec. digit */
                               1031                 :                : 
                               1032                 :                :     /*
                               1033                 :                :      * Both TO_NUMBER and TO_CHAR cases read the "input" string and write to
                               1034                 :                :      * the "output" buffer, but their semantics are a bit different.  Notably,
                               1035                 :                :      * in TO_NUMBER the input string is not null-terminated, so we need
                               1036                 :                :      * input_end to identify where to stop.
                               1037                 :                :      */
                               1038                 :                :     const char *input,          /* data input string */
                               1039                 :                :                *input_p,        /* pointer to current input position */
                               1040                 :                :                *input_end;      /* end+1 of "input" */
                               1041                 :                : 
                               1042                 :                :     StringInfo  output;         /* data output buffer */
                               1043                 :                : 
                               1044                 :                :     const char *last_relevant,  /* last relevant number after decimal point */
                               1045                 :                : 
                               1046                 :                :                *L_negative_sign,    /* Locale */
                               1047                 :                :                *L_positive_sign,
                               1048                 :                :                *decimal,
                               1049                 :                :                *L_thousands_sep,
                               1050                 :                :                *L_currency_symbol;
                               1051                 :                : } NUMProc;
                               1052                 :                : 
                               1053                 :                : /* Return flags for DCH_from_char() */
                               1054                 :                : #define DCH_DATED   0x01
                               1055                 :                : #define DCH_TIMED   0x02
                               1056                 :                : #define DCH_ZONED   0x04
                               1057                 :                : 
                               1058                 :                : /*
                               1059                 :                :  * These macros are used in NUM_processor_from_char() and its subsidiary routines.
                               1060                 :                :  * OVERLOAD_TEST: true if we've reached end of input string
                               1061                 :                :  * AMOUNT_TEST(s): true if at least s bytes remain in string
                               1062                 :                :  */
                               1063                 :                : #define OVERLOAD_TEST   (Np->input_p >= Np->input_end)
                               1064                 :                : #define AMOUNT_TEST(s)  (Np->input_p <= Np->input_end - (s))
                               1065                 :                : 
                               1066                 :                : 
                               1067                 :                : /*
                               1068                 :                :  * Functions
                               1069                 :                :  */
                               1070                 :                : static const KeyWord *index_seq_search(const char *str, const KeyWord *kw,
                               1071                 :                :                                        const int *index);
                               1072                 :                : static const KeySuffix *suff_search(const char *str, const KeySuffix *suf, enum KeySuffixType type);
                               1073                 :                : static bool is_separator_char(const char *str);
                               1074                 :                : static void NUMDesc_prepare(NUMDesc *num, FormatNode *n);
                               1075                 :                : static void parse_format(FormatNode *node, const char *str, const KeyWord *kw,
                               1076                 :                :                          const KeySuffix *suf, const int *index, uint32 flags, NUMDesc *Num);
                               1077                 :                : 
                               1078                 :                : static void DCH_to_char(const FormatNode *node, bool is_interval, Oid collid,
                               1079                 :                :                         const TmToChar *in, StringInfo out);
                               1080                 :                : static void DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
                               1081                 :                :                           Oid collid, bool std, Node *escontext);
                               1082                 :                : 
                               1083                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               1084                 :                : static void dump_index(const KeyWord *k, const int *index);
                               1085                 :                : static void dump_node(FormatNode *node, int max);
                               1086                 :                : #endif
                               1087                 :                : 
                               1088                 :                : static const char *get_th(const char *num, enum TH_Case type);
                               1089                 :                : static void str_numth(StringInfo dest, int start, enum TH_Case type);
                               1090                 :                : static int  adjust_partial_year_to_2020(int year);
                               1091                 :                : static size_t strspace_len(const char *str);
                               1092                 :                : static bool from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode,
                               1093                 :                :                                Node *escontext);
                               1094                 :                : static bool from_char_set_int(int *dest, const int value, const FormatNode *node,
                               1095                 :                :                               Node *escontext);
                               1096                 :                : static int  from_char_parse_int_len(int *dest, const char **src, const size_t len,
                               1097                 :                :                                     FormatNode *node, Node *escontext);
                               1098                 :                : static int  from_char_parse_int(int *dest, const char **src, FormatNode *node,
                               1099                 :                :                                 Node *escontext);
                               1100                 :                : static int  seq_search_ascii(const char *name, const char *const *array, size_t *len);
                               1101                 :                : static int  seq_search_localized(const char *name, char **array, size_t *len,
                               1102                 :                :                                  Oid collid);
                               1103                 :                : static bool from_char_seq_search(int *dest, const char **src,
                               1104                 :                :                                  const char *const *array,
                               1105                 :                :                                  char **localized_array, Oid collid,
                               1106                 :                :                                  FormatNode *node, Node *escontext);
                               1107                 :                : static bool do_to_timestamp(const text *date_txt, const text *fmt, Oid collid, bool std,
                               1108                 :                :                             struct pg_tm *tm, fsec_t *fsec, struct fmt_tz *tz,
                               1109                 :                :                             int *fprec, uint32 *flags, Node *escontext);
                               1110                 :                : static void fill_str(char *str, int c, int max);
                               1111                 :                : static FormatNode *NUM_cache(int len, NUMDesc *Num, const text *pars_str, bool *shouldFree);
                               1112                 :                : static char *int_to_roman(int number);
                               1113                 :                : static int  roman_to_int(NUMProc *Np);
                               1114                 :                : static void NUM_prepare_locale(NUMProc *Np);
                               1115                 :                : static const char *get_last_relevant_decnum(const char *num);
                               1116                 :                : static void NUM_numpart_from_char(NUMProc *Np, int id);
                               1117                 :                : static void NUM_numpart_to_char(NUMProc *Np, int id);
                               1118                 :                : static void NUM_processor_from_char(const FormatNode *node, NUMDesc *Num,
                               1119                 :                :                                     const char *input, size_t input_len,
                               1120                 :                :                                     StringInfo output,
                               1121                 :                :                                     Oid collid);
                               1122                 :                : static void NUM_processor_to_char(const FormatNode *node, NUMDesc *Num,
                               1123                 :                :                                   const char *input, StringInfo output,
                               1124                 :                :                                   int out_pre_spaces, int sign, Oid collid);
                               1125                 :                : static DCHCacheEntry *DCH_cache_getnew(const char *str, bool std);
                               1126                 :                : static DCHCacheEntry *DCH_cache_search(const char *str, bool std);
                               1127                 :                : static DCHCacheEntry *DCH_cache_fetch(const char *str, bool std);
                               1128                 :                : static NUMCacheEntry *NUM_cache_getnew(const char *str);
                               1129                 :                : static NUMCacheEntry *NUM_cache_search(const char *str);
                               1130                 :                : static NUMCacheEntry *NUM_cache_fetch(const char *str);
                               1131                 :                : 
                               1132                 :                : 
                               1133                 :                : /*
                               1134                 :                :  * Fast sequential search, use index for data selection which
                               1135                 :                :  * go to seq. cycle (it is very fast for unwanted strings)
                               1136                 :                :  * (can't be used binary search in format parsing)
                               1137                 :                :  */
                               1138                 :                : static const KeyWord *
 3620 tgl@sss.pgh.pa.us        1139                 :          20541 : index_seq_search(const char *str, const KeyWord *kw, const int *index)
                               1140                 :                : {
                               1141                 :                :     int         poz;
                               1142                 :                : 
 9633 bruce@momjian.us         1143   [ +  +  -  + ]:          20541 :     if (!KeyWord_INDEX_FILTER(*str))
 8268 neilc@samurai.com        1144                 :           4726 :         return NULL;
                               1145                 :                : 
  302 peter@eisentraut.org     1146         [ +  + ]:          15815 :     if ((poz = index[*str - ' ']) > -1)
                               1147                 :                :     {
 7824 tgl@sss.pgh.pa.us        1148                 :          14420 :         const KeyWord *k = kw + poz;
                               1149                 :                : 
                               1150                 :                :         do
                               1151                 :                :         {
 5357 peter_e@gmx.net          1152         [ +  + ]:          19192 :             if (strncmp(str, k->name, k->len) == 0)
 9711 bruce@momjian.us         1153                 :          14332 :                 return k;
                               1154                 :           4860 :             k++;
                               1155         [ -  + ]:           4860 :             if (!k->name)
 8268 neilc@samurai.com        1156                 :UBC           0 :                 return NULL;
 9633 bruce@momjian.us         1157         [ +  + ]:CBC        4860 :         } while (*str == *k->name);
                               1158                 :                :     }
 8268 neilc@samurai.com        1159                 :           1483 :     return NULL;
                               1160                 :                : }
                               1161                 :                : 
                               1162                 :                : static const KeySuffix *
  300 peter@eisentraut.org     1163                 :           7771 : suff_search(const char *str, const KeySuffix *suf, enum KeySuffixType type)
                               1164                 :                : {
  303                          1165         [ +  + ]:          60256 :     for (const KeySuffix *s = suf; s->name != NULL; s++)
                               1166                 :                :     {
 9711 bruce@momjian.us         1167         [ +  + ]:          52777 :         if (s->type != type)
                               1168                 :          24925 :             continue;
                               1169                 :                : 
 5357 peter_e@gmx.net          1170         [ +  + ]:          27852 :         if (strncmp(str, s->name, s->len) == 0)
 9711 bruce@momjian.us         1171                 :            292 :             return s;
                               1172                 :                :     }
 8268 neilc@samurai.com        1173                 :           7479 :     return NULL;
                               1174                 :                : }
                               1175                 :                : 
                               1176                 :                : static bool
 2909 akorotkov@postgresql     1177                 :           4357 : is_separator_char(const char *str)
                               1178                 :                : {
                               1179                 :                :     /* ASCII printable character, but not letter or digit */
                               1180         [ +  - ]:           3235 :     return (*str > 0x20 && *str < 0x7F &&
                               1181   [ +  +  +  + ]:           3235 :             !(*str >= 'A' && *str <= 'Z') &&
                               1182   [ +  +  +  +  :          10639 :             !(*str >= 'a' && *str <= 'z') &&
                                              -  + ]
                               1183   [ +  +  +  + ]:           3047 :             !(*str >= '0' && *str <= '9'));
                               1184                 :                : }
                               1185                 :                : 
                               1186                 :                : /*
                               1187                 :                :  * Prepare NUMDesc (number description struct) via FormatNode struct
                               1188                 :                :  */
                               1189                 :                : static void
 4367 bruce@momjian.us         1190                 :          11326 : NUMDesc_prepare(NUMDesc *num, FormatNode *n)
                               1191                 :                : {
 9711                          1192         [ -  + ]:          11326 :     if (n->type != NODE_TYPE_ACTION)
 9711 bruce@momjian.us         1193                 :UBC           0 :         return;
                               1194                 :                : 
 3620 tgl@sss.pgh.pa.us        1195   [ -  +  -  - ]:CBC       11326 :     if (IS_EEEE(num) && n->key->id != NUM_E)
 3620 tgl@sss.pgh.pa.us        1196         [ #  # ]:UBC           0 :         ereport(ERROR,
                               1197                 :                :                 (errcode(ERRCODE_SYNTAX_ERROR),
                               1198                 :                :                  errmsg("\"EEEE\" must be the last pattern used")));
                               1199                 :                : 
 3620 tgl@sss.pgh.pa.us        1200   [ +  +  -  +  :CBC       11326 :     switch (n->key->id)
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                              +  + ]
                               1201                 :                :     {
                               1202                 :           9654 :         case NUM_9:
                               1203         [ -  + ]:           9654 :             if (IS_BRACKET(num))
 3620 tgl@sss.pgh.pa.us        1204         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1205                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1206                 :                :                          errmsg("\"9\" must be ahead of \"PR\"")));
 3620 tgl@sss.pgh.pa.us        1207         [ +  + ]:CBC        9654 :             if (IS_MULTI(num))
                               1208                 :                :             {
                               1209                 :             24 :                 ++num->multi;
 6026 bruce@momjian.us         1210                 :             24 :                 break;
                               1211                 :                :             }
 3620 tgl@sss.pgh.pa.us        1212         [ +  + ]:           9630 :             if (IS_DECIMAL(num))
                               1213                 :           3320 :                 ++num->post;
                               1214                 :                :             else
                               1215                 :           6310 :                 ++num->pre;
                               1216                 :           9630 :             break;
                               1217                 :                : 
                               1218                 :            345 :         case NUM_0:
                               1219         [ -  + ]:            345 :             if (IS_BRACKET(num))
 3620 tgl@sss.pgh.pa.us        1220         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1221                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1222                 :                :                          errmsg("\"0\" must be ahead of \"PR\"")));
 3620 tgl@sss.pgh.pa.us        1223   [ +  +  +  + ]:CBC         345 :             if (!IS_ZERO(num) && !IS_DECIMAL(num))
                               1224                 :                :             {
                               1225                 :             73 :                 num->flag |= NUM_F_ZERO;
                               1226                 :             73 :                 num->zero_start = num->pre + 1;
                               1227                 :                :             }
                               1228         [ +  + ]:            345 :             if (!IS_DECIMAL(num))
                               1229                 :            233 :                 ++num->pre;
                               1230                 :                :             else
                               1231                 :            112 :                 ++num->post;
                               1232                 :                : 
                               1233                 :            345 :             num->zero_end = num->pre + num->post;
                               1234                 :            345 :             break;
                               1235                 :                : 
 3620 tgl@sss.pgh.pa.us        1236                 :UBC           0 :         case NUM_B:
  302 peter@eisentraut.org     1237   [ #  #  #  #  :              0 :             if (num->pre == 0 && num->post == 0 && !IS_ZERO(num))
                                              #  # ]
 3620 tgl@sss.pgh.pa.us        1238                 :              0 :                 num->flag |= NUM_F_BLANK;
                               1239                 :              0 :             break;
                               1240                 :                : 
 3620 tgl@sss.pgh.pa.us        1241                 :CBC          59 :         case NUM_D:
                               1242                 :             59 :             num->flag |= NUM_F_LDECIMAL;
 3298 peter_e@gmx.net          1243                 :             59 :             num->need_locale = true;
                               1244                 :                :             pg_fallthrough;
 3620 tgl@sss.pgh.pa.us        1245                 :            323 :         case NUM_DEC:
                               1246         [ -  + ]:            323 :             if (IS_DECIMAL(num))
 3620 tgl@sss.pgh.pa.us        1247         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1248                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1249                 :                :                          errmsg("multiple decimal points")));
 3620 tgl@sss.pgh.pa.us        1250         [ -  + ]:CBC         323 :             if (IS_MULTI(num))
 3620 tgl@sss.pgh.pa.us        1251         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1252                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1253                 :                :                          errmsg("cannot use \"V\" and decimal point together")));
 3620 tgl@sss.pgh.pa.us        1254                 :CBC         323 :             num->flag |= NUM_F_DECIMAL;
                               1255                 :            323 :             break;
                               1256                 :                : 
                               1257                 :            173 :         case NUM_FM:
                               1258                 :            173 :             num->flag |= NUM_F_FILLMODE;
                               1259                 :            173 :             break;
                               1260                 :                : 
                               1261                 :            150 :         case NUM_S:
                               1262         [ -  + ]:            150 :             if (IS_LSIGN(num))
 3620 tgl@sss.pgh.pa.us        1263         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1264                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1265                 :                :                          errmsg("cannot use \"S\" twice")));
 3620 tgl@sss.pgh.pa.us        1266   [ +  -  +  -  :CBC         150 :             if (IS_PLUS(num) || IS_MINUS(num) || IS_BRACKET(num))
                                              -  + ]
 3620 tgl@sss.pgh.pa.us        1267         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1268                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1269                 :                :                          errmsg("cannot use \"S\" and \"PL\"/\"MI\"/\"SG\"/\"PR\" together")));
 3620 tgl@sss.pgh.pa.us        1270         [ +  + ]:CBC         150 :             if (!IS_DECIMAL(num))
                               1271                 :                :             {
                               1272                 :            128 :                 num->lsign = NUM_LSIGN_PRE;
                               1273                 :            128 :                 num->pre_lsign_num = num->pre;
 3298 peter_e@gmx.net          1274                 :            128 :                 num->need_locale = true;
 3620 tgl@sss.pgh.pa.us        1275                 :            128 :                 num->flag |= NUM_F_LSIGN;
                               1276                 :                :             }
                               1277         [ +  - ]:             22 :             else if (num->lsign == NUM_LSIGN_NONE)
                               1278                 :                :             {
                               1279                 :             22 :                 num->lsign = NUM_LSIGN_POST;
 3298 peter_e@gmx.net          1280                 :             22 :                 num->need_locale = true;
 3620 tgl@sss.pgh.pa.us        1281                 :             22 :                 num->flag |= NUM_F_LSIGN;
                               1282                 :                :             }
                               1283                 :            150 :             break;
                               1284                 :                : 
                               1285                 :             24 :         case NUM_MI:
                               1286         [ -  + ]:             24 :             if (IS_LSIGN(num))
 3620 tgl@sss.pgh.pa.us        1287         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1288                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1289                 :                :                          errmsg("cannot use \"S\" and \"MI\" together")));
 3620 tgl@sss.pgh.pa.us        1290                 :CBC          24 :             num->flag |= NUM_F_MINUS;
                               1291         [ +  + ]:             24 :             if (IS_DECIMAL(num))
                               1292                 :              4 :                 num->flag |= NUM_F_MINUS_POST;
                               1293                 :             24 :             break;
                               1294                 :                : 
                               1295                 :              4 :         case NUM_PL:
                               1296         [ -  + ]:              4 :             if (IS_LSIGN(num))
 3620 tgl@sss.pgh.pa.us        1297         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1298                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1299                 :                :                          errmsg("cannot use \"S\" and \"PL\" together")));
 3620 tgl@sss.pgh.pa.us        1300                 :CBC           4 :             num->flag |= NUM_F_PLUS;
                               1301         [ -  + ]:              4 :             if (IS_DECIMAL(num))
 3620 tgl@sss.pgh.pa.us        1302                 :UBC           0 :                 num->flag |= NUM_F_PLUS_POST;
 3620 tgl@sss.pgh.pa.us        1303                 :CBC           4 :             break;
                               1304                 :                : 
                               1305                 :             16 :         case NUM_SG:
                               1306         [ -  + ]:             16 :             if (IS_LSIGN(num))
 3620 tgl@sss.pgh.pa.us        1307         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1308                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1309                 :                :                          errmsg("cannot use \"S\" and \"SG\" together")));
 3620 tgl@sss.pgh.pa.us        1310                 :CBC          16 :             num->flag |= NUM_F_MINUS;
                               1311                 :             16 :             num->flag |= NUM_F_PLUS;
                               1312                 :             16 :             break;
                               1313                 :                : 
                               1314                 :             24 :         case NUM_PR:
                               1315   [ +  -  +  -  :             24 :             if (IS_LSIGN(num) || IS_PLUS(num) || IS_MINUS(num))
                                              -  + ]
 3620 tgl@sss.pgh.pa.us        1316         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1317                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1318                 :                :                          errmsg("cannot use \"PR\" and \"S\"/\"PL\"/\"MI\"/\"SG\" together")));
 3620 tgl@sss.pgh.pa.us        1319                 :CBC          24 :             num->flag |= NUM_F_BRACKET;
                               1320                 :             24 :             break;
                               1321                 :                : 
                               1322                 :             44 :         case NUM_rn:
                               1323                 :                :         case NUM_RN:
  582                          1324         [ +  + ]:             44 :             if (IS_ROMAN(num))
                               1325         [ +  - ]:              4 :                 ereport(ERROR,
                               1326                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1327                 :                :                          errmsg("cannot use \"RN\" twice")));
 3620                          1328                 :             40 :             num->flag |= NUM_F_ROMAN;
                               1329                 :             40 :             break;
                               1330                 :                : 
                               1331                 :            469 :         case NUM_L:
                               1332                 :                :         case NUM_G:
 3298 peter_e@gmx.net          1333                 :            469 :             num->need_locale = true;
 3620 tgl@sss.pgh.pa.us        1334                 :            469 :             break;
                               1335                 :                : 
                               1336                 :             12 :         case NUM_V:
                               1337         [ -  + ]:             12 :             if (IS_DECIMAL(num))
 3620 tgl@sss.pgh.pa.us        1338         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1339                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1340                 :                :                          errmsg("cannot use \"V\" and decimal point together")));
 3620 tgl@sss.pgh.pa.us        1341                 :CBC          12 :             num->flag |= NUM_F_MULTI;
                               1342                 :             12 :             break;
                               1343                 :                : 
                               1344                 :             12 :         case NUM_E:
                               1345         [ -  + ]:             12 :             if (IS_EEEE(num))
 3620 tgl@sss.pgh.pa.us        1346         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1347                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1348                 :                :                          errmsg("cannot use \"EEEE\" twice")));
 3620 tgl@sss.pgh.pa.us        1349   [ +  -  +  -  :CBC          12 :             if (IS_BLANK(num) || IS_FILLMODE(num) || IS_LSIGN(num) ||
                                              +  - ]
                               1350   [ +  -  +  -  :             12 :                 IS_BRACKET(num) || IS_MINUS(num) || IS_PLUS(num) ||
                                              +  - ]
                               1351   [ +  -  -  + ]:             12 :                 IS_ROMAN(num) || IS_MULTI(num))
 3620 tgl@sss.pgh.pa.us        1352         [ #  # ]:UBC           0 :                 ereport(ERROR,
                               1353                 :                :                         (errcode(ERRCODE_SYNTAX_ERROR),
                               1354                 :                :                          errmsg("\"EEEE\" is incompatible with other formats"),
                               1355                 :                :                          errdetail("\"EEEE\" may only be used together with digit and decimal point patterns.")));
 3620 tgl@sss.pgh.pa.us        1356                 :CBC          12 :             num->flag |= NUM_F_EEEE;
                               1357                 :             12 :             break;
                               1358                 :                :     }
                               1359                 :                : 
  582                          1360         [ +  + ]:          11322 :     if (IS_ROMAN(num) &&
                               1361         [ +  + ]:             40 :         (num->flag & ~(NUM_F_ROMAN | NUM_F_FILLMODE)) != 0)
                               1362         [ +  - ]:              4 :         ereport(ERROR,
                               1363                 :                :                 (errcode(ERRCODE_SYNTAX_ERROR),
                               1364                 :                :                  errmsg("\"RN\" is incompatible with other formats"),
                               1365                 :                :                  errdetail("\"RN\" may only be used together with \"FM\".")));
                               1366                 :                : }
                               1367                 :                : 
                               1368                 :                : /*
                               1369                 :                :  * Format parser, search small keywords and keyword's suffixes, and make
                               1370                 :                :  * format-node tree.
                               1371                 :                :  *
                               1372                 :                :  * for DATE-TIME & NUMBER version
                               1373                 :                :  */
                               1374                 :                : static void
 3620                          1375                 :           1232 : parse_format(FormatNode *node, const char *str, const KeyWord *kw,
                               1376                 :                :              const KeySuffix *suf, const int *index, uint32 flags, NUMDesc *Num)
                               1377                 :                : {
                               1378                 :                :     FormatNode *n;
                               1379                 :                : 
                               1380                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               1381                 :                :     elog(DEBUG_elog_output, "to_char/number(): run parser");
                               1382                 :                : #endif
                               1383                 :                : 
 9711 bruce@momjian.us         1384                 :           1232 :     n = node;
                               1385                 :                : 
 9633                          1386         [ +  + ]:          21761 :     while (*str)
                               1387                 :                :     {
 3204 tgl@sss.pgh.pa.us        1388                 :          20541 :         int         suffix = 0;
                               1389                 :                :         const KeySuffix *s;
                               1390                 :                : 
                               1391                 :                :         /*
                               1392                 :                :          * Prefix
                               1393                 :                :          */
 2528 akorotkov@postgresql     1394   [ +  +  +  + ]:          25908 :         if ((flags & DCH_FLAG) &&
 3204 tgl@sss.pgh.pa.us        1395                 :           5367 :             (s = suff_search(str, suf, SUFFTYPE_PREFIX)) != NULL)
                               1396                 :                :         {
 9711 bruce@momjian.us         1397                 :            264 :             suffix |= s->id;
                               1398         [ +  - ]:            264 :             if (s->len)
                               1399                 :            264 :                 str += s->len;
                               1400                 :                :         }
                               1401                 :                : 
                               1402                 :                :         /*
                               1403                 :                :          * Keyword
                               1404                 :                :          */
 9633                          1405   [ +  -  +  + ]:          20541 :         if (*str && (n->key = index_seq_search(str, kw, index)) != NULL)
                               1406                 :                :         {
 9711                          1407                 :          14332 :             n->type = NODE_TYPE_ACTION;
 3204 tgl@sss.pgh.pa.us        1408                 :          14332 :             n->suffix = suffix;
 9711 bruce@momjian.us         1409         [ +  - ]:          14332 :             if (n->key->len)
                               1410                 :          14332 :                 str += n->key->len;
                               1411                 :                : 
                               1412                 :                :             /*
                               1413                 :                :              * NUM version: Prepare global NUMDesc struct
                               1414                 :                :              */
 2528 akorotkov@postgresql     1415         [ +  + ]:          14332 :             if (flags & NUM_FLAG)
 4367 bruce@momjian.us         1416                 :          11326 :                 NUMDesc_prepare(Num, n);
                               1417                 :                : 
                               1418                 :                :             /*
                               1419                 :                :              * Postfix
                               1420                 :                :              */
 2528 akorotkov@postgresql     1421   [ +  +  +  +  :          16728 :             if ((flags & DCH_FLAG) && *str &&
                                              +  + ]
 3204 tgl@sss.pgh.pa.us        1422                 :           2404 :                 (s = suff_search(str, suf, SUFFTYPE_POSTFIX)) != NULL)
                               1423                 :                :             {
                               1424                 :             28 :                 n->suffix |= s->id;
 9711 bruce@momjian.us         1425         [ +  - ]:             28 :                 if (s->len)
                               1426                 :             28 :                     str += s->len;
                               1427                 :                :             }
                               1428                 :                : 
 3204 tgl@sss.pgh.pa.us        1429                 :          14324 :             n++;
                               1430                 :                :         }
 9633 bruce@momjian.us         1431         [ +  - ]:           6209 :         else if (*str)
                               1432                 :                :         {
                               1433                 :                :             int         chlen;
                               1434                 :                : 
 2158 akorotkov@postgresql     1435   [ +  +  +  + ]:           6209 :             if ((flags & STD_FLAG) && *str != '"')
                               1436                 :                :             {
                               1437                 :                :                 /*
                               1438                 :                :                  * Standard mode, allow only following separators: "-./,':; ".
                               1439                 :                :                  * However, we support double quotes even in standard mode
                               1440                 :                :                  * (see below).  This is our extension of standard mode.
                               1441                 :                :                  */
 2528                          1442         [ +  + ]:            380 :                 if (strchr("-./,':; ", *str) == NULL)
                               1443         [ +  - ]:              4 :                     ereport(ERROR,
                               1444                 :                :                             (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               1445                 :                :                              errmsg("invalid datetime format separator: \"%s\"",
                               1446                 :                :                                     pnstrdup(str, pg_mblen_cstr(str)))));
                               1447                 :                : 
                               1448         [ +  + ]:            376 :                 if (*str == ' ')
                               1449                 :             60 :                     n->type = NODE_TYPE_SPACE;
                               1450                 :                :                 else
                               1451                 :            316 :                     n->type = NODE_TYPE_SEPARATOR;
                               1452                 :                : 
                               1453                 :            376 :                 n->character[0] = *str;
                               1454                 :            376 :                 n->character[1] = '\0';
                               1455                 :            376 :                 n->key = NULL;
                               1456                 :            376 :                 n->suffix = 0;
                               1457                 :            376 :                 n++;
                               1458                 :            376 :                 str++;
                               1459                 :                :             }
                               1460         [ +  + ]:           5829 :             else if (*str == '"')
                               1461                 :                :             {
                               1462                 :                :                 /*
                               1463                 :                :                  * Process double-quoted literal string, if any
                               1464                 :                :                  */
 3204 tgl@sss.pgh.pa.us        1465                 :            256 :                 str++;
                               1466         [ +  + ]:           2944 :                 while (*str)
                               1467                 :                :                 {
                               1468         [ +  + ]:           2940 :                     if (*str == '"')
                               1469                 :                :                     {
 9711 bruce@momjian.us         1470                 :            252 :                         str++;
                               1471                 :            252 :                         break;
                               1472                 :                :                     }
                               1473                 :                :                     /* backslash quotes the next character, if any */
 3204 tgl@sss.pgh.pa.us        1474   [ +  +  +  - ]:           2688 :                     if (*str == '\\' && *(str + 1))
                               1475                 :            160 :                         str++;
  232 tmunro@postgresql.or     1476                 :           2688 :                     chlen = pg_mblen_cstr(str);
 9711 bruce@momjian.us         1477                 :           2688 :                     n->type = NODE_TYPE_CHAR;
 3204 tgl@sss.pgh.pa.us        1478                 :           2688 :                     memcpy(n->character, str, chlen);
                               1479                 :           2688 :                     n->character[chlen] = '\0';
 8268 neilc@samurai.com        1480                 :           2688 :                     n->key = NULL;
 9711 bruce@momjian.us         1481                 :           2688 :                     n->suffix = 0;
 3204 tgl@sss.pgh.pa.us        1482                 :           2688 :                     n++;
                               1483                 :           2688 :                     str += chlen;
                               1484                 :                :                 }
                               1485                 :                :             }
                               1486                 :                :             else
                               1487                 :                :             {
                               1488                 :                :                 /*
                               1489                 :                :                  * Outside double-quoted strings, backslash is only special if
                               1490                 :                :                  * it immediately precedes a double quote.
                               1491                 :                :                  */
                               1492   [ +  +  +  + ]:           5573 :                 if (*str == '\\' && *(str + 1) == '"')
                               1493                 :              8 :                     str++;
  232 tmunro@postgresql.or     1494                 :           5573 :                 chlen = pg_mblen_cstr(str);
                               1495                 :                : 
 2528 akorotkov@postgresql     1496   [ +  +  +  + ]:           5573 :                 if ((flags & DCH_FLAG) && is_separator_char(str))
 2909                          1497                 :            716 :                     n->type = NODE_TYPE_SEPARATOR;
                               1498         [ +  + ]:           4857 :                 else if (isspace((unsigned char) *str))
                               1499                 :           4666 :                     n->type = NODE_TYPE_SPACE;
                               1500                 :                :                 else
                               1501                 :            191 :                     n->type = NODE_TYPE_CHAR;
                               1502                 :                : 
 3204 tgl@sss.pgh.pa.us        1503                 :           5573 :                 memcpy(n->character, str, chlen);
                               1504                 :           5573 :                 n->character[chlen] = '\0';
 8268 neilc@samurai.com        1505                 :           5573 :                 n->key = NULL;
 3204 tgl@sss.pgh.pa.us        1506                 :           5573 :                 n->suffix = 0;
                               1507                 :           5573 :                 n++;
                               1508                 :           5573 :                 str += chlen;
                               1509                 :                :             }
                               1510                 :                :         }
                               1511                 :                :     }
                               1512                 :                : 
 9711 bruce@momjian.us         1513                 :           1220 :     n->type = NODE_TYPE_END;
                               1514                 :           1220 :     n->suffix = 0;
                               1515                 :           1220 : }
                               1516                 :                : 
                               1517                 :                : /*
                               1518                 :                :  * DEBUG: Dump the FormatNode Tree (debug)
                               1519                 :                :  */
                               1520                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               1521                 :                : 
                               1522                 :                : #define DUMP_THth(_suf) (IS_SUFFIX_TH(_suf) ? "TH" : (IS_SUFFIX_th(_suf) ? "th" : " "))
                               1523                 :                : #define DUMP_FM(_suf)   (IS_SUFFIX_FM(_suf) ? "FM" : " ")
                               1524                 :                : 
                               1525                 :                : static void
                               1526                 :                : dump_node(FormatNode *node, int max)
                               1527                 :                : {
                               1528                 :                :     FormatNode *n;
                               1529                 :                :     int         a;
                               1530                 :                : 
                               1531                 :                :     elog(DEBUG_elog_output, "to_from-char(): DUMP FORMAT");
                               1532                 :                : 
                               1533                 :                :     for (a = 0, n = node; a <= max; n++, a++)
                               1534                 :                :     {
                               1535                 :                :         if (n->type == NODE_TYPE_ACTION)
                               1536                 :                :             elog(DEBUG_elog_output, "%d:\t NODE_TYPE_ACTION '%s'\t(%s,%s)",
                               1537                 :                :                  a, n->key->name, DUMP_THth(n->suffix), DUMP_FM(n->suffix));
                               1538                 :                :         else if (n->type == NODE_TYPE_CHAR)
                               1539                 :                :             elog(DEBUG_elog_output, "%d:\t NODE_TYPE_CHAR '%s'",
                               1540                 :                :                  a, n->character);
                               1541                 :                :         else if (n->type == NODE_TYPE_END)
                               1542                 :                :         {
                               1543                 :                :             elog(DEBUG_elog_output, "%d:\t NODE_TYPE_END", a);
                               1544                 :                :             return;
                               1545                 :                :         }
                               1546                 :                :         else
                               1547                 :                :             elog(DEBUG_elog_output, "%d:\t unknown NODE!", a);
                               1548                 :                :     }
                               1549                 :                : }
                               1550                 :                : #endif                          /* DEBUG */
                               1551                 :                : 
                               1552                 :                : /*****************************************************************************
                               1553                 :                :  *          Private utils
                               1554                 :                :  *****************************************************************************/
                               1555                 :                : 
                               1556                 :                : /*
                               1557                 :                :  * Return ST/ND/RD/TH for simple (1..99) numbers
                               1558                 :                :  */
                               1559                 :                : static const char *
  300 peter@eisentraut.org     1560                 :           1556 : get_th(const char *num, enum TH_Case type)
                               1561                 :                : {
  302                          1562                 :           1556 :     size_t      len = strlen(num);
                               1563                 :                :     char        last;
                               1564                 :                : 
  374                          1565         [ -  + ]:           1556 :     Assert(len > 0);
                               1566                 :                : 
  302                          1567                 :           1556 :     last = num[len - 1];
 9711 bruce@momjian.us         1568         [ -  + ]:           1556 :     if (!isdigit((unsigned char) last))
 8432 tgl@sss.pgh.pa.us        1569         [ #  # ]:UBC           0 :         ereport(ERROR,
                               1570                 :                :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                               1571                 :                :                  errmsg("\"%s\" is not a number", num)));
                               1572                 :                : 
                               1573                 :                :     /*
                               1574                 :                :      * All "teens" (<x>1[0-9]) get 'TH/th', while <x>[02-9][123] still get
                               1575                 :                :      * 'ST/st', 'ND/nd', 'RD/rd', respectively
                               1576                 :                :      */
  302 peter@eisentraut.org     1577   [ +  -  +  + ]:CBC        1556 :     if (len > 1 && num[len - 2] == '1')
 9633 bruce@momjian.us         1578                 :             88 :         last = 0;
                               1579                 :                : 
                               1580   [ +  +  +  + ]:           1556 :     switch (last)
                               1581                 :                :     {
 9711                          1582                 :             64 :         case '1':
 9633                          1583         [ +  + ]:             64 :             if (type == TH_UPPER)
                               1584                 :             16 :                 return numTH[0];
 9711                          1585                 :             48 :             return numth[0];
                               1586                 :             32 :         case '2':
 9633                          1587         [ -  + ]:             32 :             if (type == TH_UPPER)
 9633 bruce@momjian.us         1588                 :UBC           0 :                 return numTH[1];
 9711 bruce@momjian.us         1589                 :CBC          32 :             return numth[1];
                               1590                 :             24 :         case '3':
 9633                          1591         [ +  + ]:             24 :             if (type == TH_UPPER)
                               1592                 :              4 :                 return numTH[2];
                               1593                 :             20 :             return numth[2];
 9711                          1594                 :           1436 :         default:
 9633                          1595         [ +  + ]:           1436 :             if (type == TH_UPPER)
                               1596                 :            504 :                 return numTH[3];
 9711                          1597                 :            932 :             return numth[3];
                               1598                 :                :     }
                               1599                 :                : }
                               1600                 :                : 
                               1601                 :                : /*
                               1602                 :                :  * Convert string-number to ordinal string-number
                               1603                 :                :  *
                               1604                 :                :  * The number we are considering starts at offset "start" in dest.
                               1605                 :                :  */
                               1606                 :                : static void
    7 tgl@sss.pgh.pa.us        1607                 :GNC        1524 : str_numth(StringInfo dest, int start, enum TH_Case type)
                               1608                 :                : {
                               1609         [ -  + ]:           1524 :     Assert(start < dest->len);
                               1610                 :           1524 :     appendStringInfoString(dest, get_th(dest->data + start, type));
 9711 bruce@momjian.us         1611                 :GIC        1524 : }
                               1612                 :                : 
                               1613                 :                : /*****************************************************************************
                               1614                 :                :  *          upper/lower/initcap functions
                               1615                 :                :  *****************************************************************************/
                               1616                 :                : 
                               1617                 :                : /*
                               1618                 :                :  * collation-aware, wide-character-aware lower function
                               1619                 :                :  *
                               1620                 :                :  * We pass the number of bytes so we can pass varlena and char*
                               1621                 :                :  * to this function.  The result is a palloc'd, null-terminated string.
                               1622                 :                :  */
                               1623                 :                : char *
 5679 peter_e@gmx.net          1624                 :CBC      251930 : str_tolower(const char *buff, size_t nbytes, Oid collid)
                               1625                 :                : {
                               1626                 :                :     char       *result;
                               1627                 :                :     pg_locale_t mylocale;
                               1628                 :                : 
 8742 bruce@momjian.us         1629         [ -  + ]:         251930 :     if (!buff)
 8742 bruce@momjian.us         1630                 :UBC           0 :         return NULL;
                               1631                 :                : 
 1680 peter@eisentraut.org     1632         [ -  + ]:CBC      251930 :     if (!OidIsValid(collid))
                               1633                 :                :     {
                               1634                 :                :         /*
                               1635                 :                :          * This typically means that the parser could not resolve a conflict
                               1636                 :                :          * of implicit collations, so report it that way.
                               1637                 :                :          */
 1680 peter@eisentraut.org     1638         [ #  # ]:UBC           0 :         ereport(ERROR,
                               1639                 :                :                 (errcode(ERRCODE_INDETERMINATE_COLLATION),
                               1640                 :                :                  errmsg("could not determine which collation to use for %s function",
                               1641                 :                :                         "lower()"),
                               1642                 :                :                  errhint("Use the COLLATE clause to set the collation explicitly.")));
                               1643                 :                :     }
                               1644                 :                : 
  720 jdavis@postgresql.or     1645                 :CBC      251930 :     mylocale = pg_newlocale_from_collation(collid);
                               1646                 :                : 
                               1647                 :                :     /* C/POSIX collations use this path regardless of database encoding */
                               1648         [ +  + ]:         251930 :     if (mylocale->ctype_is_c)
                               1649                 :                :     {
 4923 tgl@sss.pgh.pa.us        1650                 :          24289 :         result = asc_tolower(buff, nbytes);
                               1651                 :                :     }
                               1652                 :                :     else
                               1653                 :                :     {
  619 jdavis@postgresql.or     1654                 :         227641 :         const char *src = buff;
                               1655                 :         227641 :         size_t      srclen = nbytes;
                               1656                 :                :         size_t      dstsize;
                               1657                 :                :         char       *dst;
                               1658                 :                :         size_t      needed;
                               1659                 :                : 
                               1660                 :                :         /* first try buffer of equal size plus terminating NUL */
                               1661                 :         227641 :         dstsize = srclen + 1;
                               1662                 :         227641 :         dst = palloc(dstsize);
                               1663                 :                : 
                               1664                 :         227641 :         needed = pg_strlower(dst, dstsize, src, srclen, mylocale);
                               1665         [ +  + ]:         227641 :         if (needed + 1 > dstsize)
                               1666                 :                :         {
                               1667                 :                :             /* grow buffer if needed and retry */
                               1668                 :             66 :             dstsize = needed + 1;
                               1669                 :             66 :             dst = repalloc(dst, dstsize);
                               1670                 :             66 :             needed = pg_strlower(dst, dstsize, src, srclen, mylocale);
                               1671         [ -  + ]:             66 :             Assert(needed + 1 <= dstsize);
                               1672                 :                :         }
                               1673                 :                : 
                               1674         [ -  + ]:         227641 :         Assert(dst[needed] == '\0');
                               1675                 :         227641 :         result = dst;
                               1676                 :                :     }
                               1677                 :                : 
 6674 tgl@sss.pgh.pa.us        1678                 :         251930 :     return result;
                               1679                 :                : }
                               1680                 :                : 
                               1681                 :                : /*
                               1682                 :                :  * collation-aware, wide-character-aware upper function
                               1683                 :                :  *
                               1684                 :                :  * We pass the number of bytes so we can pass varlena and char*
                               1685                 :                :  * to this function.  The result is a palloc'd, null-terminated string.
                               1686                 :                :  */
                               1687                 :                : char *
 5679 peter_e@gmx.net          1688                 :         683774 : str_toupper(const char *buff, size_t nbytes, Oid collid)
                               1689                 :                : {
                               1690                 :                :     char       *result;
                               1691                 :                :     pg_locale_t mylocale;
                               1692                 :                : 
 8742 bruce@momjian.us         1693         [ -  + ]:         683774 :     if (!buff)
 8742 bruce@momjian.us         1694                 :UBC           0 :         return NULL;
                               1695                 :                : 
 1680 peter@eisentraut.org     1696         [ -  + ]:CBC      683774 :     if (!OidIsValid(collid))
                               1697                 :                :     {
                               1698                 :                :         /*
                               1699                 :                :          * This typically means that the parser could not resolve a conflict
                               1700                 :                :          * of implicit collations, so report it that way.
                               1701                 :                :          */
 1680 peter@eisentraut.org     1702         [ #  # ]:UBC           0 :         ereport(ERROR,
                               1703                 :                :                 (errcode(ERRCODE_INDETERMINATE_COLLATION),
                               1704                 :                :                  errmsg("could not determine which collation to use for %s function",
                               1705                 :                :                         "upper()"),
                               1706                 :                :                  errhint("Use the COLLATE clause to set the collation explicitly.")));
                               1707                 :                :     }
                               1708                 :                : 
  720 jdavis@postgresql.or     1709                 :CBC      683774 :     mylocale = pg_newlocale_from_collation(collid);
                               1710                 :                : 
                               1711                 :                :     /* C/POSIX collations use this path regardless of database encoding */
                               1712         [ +  + ]:         683774 :     if (mylocale->ctype_is_c)
                               1713                 :                :     {
 4923 tgl@sss.pgh.pa.us        1714                 :           2677 :         result = asc_toupper(buff, nbytes);
                               1715                 :                :     }
                               1716                 :                :     else
                               1717                 :                :     {
  619 jdavis@postgresql.or     1718                 :         681097 :         const char *src = buff;
                               1719                 :         681097 :         size_t      srclen = nbytes;
                               1720                 :                :         size_t      dstsize;
                               1721                 :                :         char       *dst;
                               1722                 :                :         size_t      needed;
                               1723                 :                : 
                               1724                 :                :         /* first try buffer of equal size plus terminating NUL */
                               1725                 :         681097 :         dstsize = srclen + 1;
                               1726                 :         681097 :         dst = palloc(dstsize);
                               1727                 :                : 
                               1728                 :         681097 :         needed = pg_strupper(dst, dstsize, src, srclen, mylocale);
                               1729         [ +  + ]:         681097 :         if (needed + 1 > dstsize)
                               1730                 :                :         {
                               1731                 :                :             /* grow buffer if needed and retry */
                               1732                 :              4 :             dstsize = needed + 1;
                               1733                 :              4 :             dst = repalloc(dst, dstsize);
                               1734                 :              4 :             needed = pg_strupper(dst, dstsize, src, srclen, mylocale);
                               1735         [ -  + ]:              4 :             Assert(needed + 1 <= dstsize);
                               1736                 :                :         }
                               1737                 :                : 
                               1738         [ -  + ]:         681097 :         Assert(dst[needed] == '\0');
                               1739                 :         681097 :         result = dst;
                               1740                 :                :     }
                               1741                 :                : 
 6674 tgl@sss.pgh.pa.us        1742                 :         683774 :     return result;
                               1743                 :                : }
                               1744                 :                : 
                               1745                 :                : /*
                               1746                 :                :  * collation-aware, wide-character-aware initcap function
                               1747                 :                :  *
                               1748                 :                :  * We pass the number of bytes so we can pass varlena and char*
                               1749                 :                :  * to this function.  The result is a palloc'd, null-terminated string.
                               1750                 :                :  */
                               1751                 :                : char *
 5679 peter_e@gmx.net          1752                 :            167 : str_initcap(const char *buff, size_t nbytes, Oid collid)
                               1753                 :                : {
                               1754                 :                :     char       *result;
                               1755                 :                :     pg_locale_t mylocale;
                               1756                 :                : 
 7140 bruce@momjian.us         1757         [ -  + ]:            167 :     if (!buff)
 7140 bruce@momjian.us         1758                 :UBC           0 :         return NULL;
                               1759                 :                : 
 1680 peter@eisentraut.org     1760         [ -  + ]:CBC         167 :     if (!OidIsValid(collid))
                               1761                 :                :     {
                               1762                 :                :         /*
                               1763                 :                :          * This typically means that the parser could not resolve a conflict
                               1764                 :                :          * of implicit collations, so report it that way.
                               1765                 :                :          */
 1680 peter@eisentraut.org     1766         [ #  # ]:UBC           0 :         ereport(ERROR,
                               1767                 :                :                 (errcode(ERRCODE_INDETERMINATE_COLLATION),
                               1768                 :                :                  errmsg("could not determine which collation to use for %s function",
                               1769                 :                :                         "initcap()"),
                               1770                 :                :                  errhint("Use the COLLATE clause to set the collation explicitly.")));
                               1771                 :                :     }
                               1772                 :                : 
  720 jdavis@postgresql.or     1773                 :CBC         167 :     mylocale = pg_newlocale_from_collation(collid);
                               1774                 :                : 
                               1775                 :                :     /* C/POSIX collations use this path regardless of database encoding */
                               1776         [ +  + ]:            167 :     if (mylocale->ctype_is_c)
                               1777                 :                :     {
 4923 tgl@sss.pgh.pa.us        1778                 :             16 :         result = asc_initcap(buff, nbytes);
                               1779                 :                :     }
                               1780                 :                :     else
                               1781                 :                :     {
  619 jdavis@postgresql.or     1782                 :            151 :         const char *src = buff;
                               1783                 :            151 :         size_t      srclen = nbytes;
                               1784                 :                :         size_t      dstsize;
                               1785                 :                :         char       *dst;
                               1786                 :                :         size_t      needed;
                               1787                 :                : 
                               1788                 :                :         /* first try buffer of equal size plus terminating NUL */
                               1789                 :            151 :         dstsize = srclen + 1;
                               1790                 :            151 :         dst = palloc(dstsize);
                               1791                 :                : 
                               1792                 :            151 :         needed = pg_strtitle(dst, dstsize, src, srclen, mylocale);
                               1793         [ +  + ]:            151 :         if (needed + 1 > dstsize)
                               1794                 :                :         {
                               1795                 :                :             /* grow buffer if needed and retry */
                               1796                 :             20 :             dstsize = needed + 1;
                               1797                 :             20 :             dst = repalloc(dst, dstsize);
                               1798                 :             20 :             needed = pg_strtitle(dst, dstsize, src, srclen, mylocale);
                               1799         [ -  + ]:             20 :             Assert(needed + 1 <= dstsize);
                               1800                 :                :         }
                               1801                 :                : 
                               1802         [ -  + ]:            151 :         Assert(dst[needed] == '\0');
                               1803                 :            151 :         result = dst;
                               1804                 :                :     }
                               1805                 :                : 
 6674 tgl@sss.pgh.pa.us        1806                 :            167 :     return result;
                               1807                 :                : }
                               1808                 :                : 
                               1809                 :                : /*
                               1810                 :                :  * collation-aware, wide-character-aware case folding
                               1811                 :                :  *
                               1812                 :                :  * We pass the number of bytes so we can pass varlena and char*
                               1813                 :                :  * to this function.  The result is a palloc'd, null-terminated string.
                               1814                 :                :  */
                               1815                 :                : char *
  580 jdavis@postgresql.or     1816                 :             20 : str_casefold(const char *buff, size_t nbytes, Oid collid)
                               1817                 :                : {
                               1818                 :                :     char       *result;
                               1819                 :                :     pg_locale_t mylocale;
                               1820                 :                : 
                               1821         [ -  + ]:             20 :     if (!buff)
  580 jdavis@postgresql.or     1822                 :UBC           0 :         return NULL;
                               1823                 :                : 
  580 jdavis@postgresql.or     1824         [ -  + ]:CBC          20 :     if (!OidIsValid(collid))
                               1825                 :                :     {
                               1826                 :                :         /*
                               1827                 :                :          * This typically means that the parser could not resolve a conflict
                               1828                 :                :          * of implicit collations, so report it that way.
                               1829                 :                :          */
  580 jdavis@postgresql.or     1830         [ #  # ]:UBC           0 :         ereport(ERROR,
                               1831                 :                :                 (errcode(ERRCODE_INDETERMINATE_COLLATION),
                               1832                 :                :                  errmsg("could not determine which collation to use for %s function",
                               1833                 :                :                         "casefold()"),
                               1834                 :                :                  errhint("Use the COLLATE clause to set the collation explicitly.")));
                               1835                 :                :     }
                               1836                 :                : 
  580 jdavis@postgresql.or     1837         [ -  + ]:CBC          20 :     if (GetDatabaseEncoding() != PG_UTF8)
  580 jdavis@postgresql.or     1838         [ #  # ]:UBC           0 :         ereport(ERROR,
                               1839                 :                :                 (errcode(ERRCODE_SYNTAX_ERROR),
                               1840                 :                :                  errmsg("Unicode case folding can only be performed if server encoding is UTF8")));
                               1841                 :                : 
  580 jdavis@postgresql.or     1842                 :CBC          20 :     mylocale = pg_newlocale_from_collation(collid);
                               1843                 :                : 
                               1844                 :                :     /* C/POSIX collations use this path regardless of database encoding */
                               1845         [ -  + ]:             20 :     if (mylocale->ctype_is_c)
                               1846                 :                :     {
  580 jdavis@postgresql.or     1847                 :UBC           0 :         result = asc_tolower(buff, nbytes);
                               1848                 :                :     }
                               1849                 :                :     else
                               1850                 :                :     {
  580 jdavis@postgresql.or     1851                 :CBC          20 :         const char *src = buff;
                               1852                 :             20 :         size_t      srclen = nbytes;
                               1853                 :                :         size_t      dstsize;
                               1854                 :                :         char       *dst;
                               1855                 :                :         size_t      needed;
                               1856                 :                : 
                               1857                 :                :         /* first try buffer of equal size plus terminating NUL */
                               1858                 :             20 :         dstsize = srclen + 1;
                               1859                 :             20 :         dst = palloc(dstsize);
                               1860                 :                : 
                               1861                 :             20 :         needed = pg_strfold(dst, dstsize, src, srclen, mylocale);
                               1862         [ -  + ]:             20 :         if (needed + 1 > dstsize)
                               1863                 :                :         {
                               1864                 :                :             /* grow buffer if needed and retry */
  580 jdavis@postgresql.or     1865                 :UBC           0 :             dstsize = needed + 1;
                               1866                 :              0 :             dst = repalloc(dst, dstsize);
                               1867                 :              0 :             needed = pg_strfold(dst, dstsize, src, srclen, mylocale);
                               1868         [ #  # ]:              0 :             Assert(needed + 1 <= dstsize);
                               1869                 :                :         }
                               1870                 :                : 
  580 jdavis@postgresql.or     1871         [ -  + ]:CBC          20 :         Assert(dst[needed] == '\0');
                               1872                 :             20 :         result = dst;
                               1873                 :                :     }
                               1874                 :                : 
                               1875                 :             20 :     return result;
                               1876                 :                : }
                               1877                 :                : 
                               1878                 :                : /*
                               1879                 :                :  * ASCII-only lower function
                               1880                 :                :  *
                               1881                 :                :  * We pass the number of bytes so we can pass varlena and char*
                               1882                 :                :  * to this function.  The result is a palloc'd, null-terminated string.
                               1883                 :                :  */
                               1884                 :                : char *
 4923 tgl@sss.pgh.pa.us        1885                 :          27361 : asc_tolower(const char *buff, size_t nbytes)
                               1886                 :                : {
                               1887                 :                :     char       *result;
                               1888                 :                : 
                               1889         [ -  + ]:          27361 :     if (!buff)
 4923 tgl@sss.pgh.pa.us        1890                 :UBC           0 :         return NULL;
                               1891                 :                : 
 4923 tgl@sss.pgh.pa.us        1892                 :CBC       27361 :     result = pnstrdup(buff, nbytes);
                               1893                 :                : 
  303 peter@eisentraut.org     1894         [ +  + ]:         284090 :     for (char *p = result; *p; p++)
 4923 tgl@sss.pgh.pa.us        1895                 :         256729 :         *p = pg_ascii_tolower((unsigned char) *p);
                               1896                 :                : 
                               1897                 :          27361 :     return result;
                               1898                 :                : }
                               1899                 :                : 
                               1900                 :                : /*
                               1901                 :                :  * ASCII-only upper function
                               1902                 :                :  *
                               1903                 :                :  * We pass the number of bytes so we can pass varlena and char*
                               1904                 :                :  * to this function.  The result is a palloc'd, null-terminated string.
                               1905                 :                :  */
                               1906                 :                : char *
                               1907                 :           5725 : asc_toupper(const char *buff, size_t nbytes)
                               1908                 :                : {
                               1909                 :                :     char       *result;
                               1910                 :                : 
                               1911         [ -  + ]:           5725 :     if (!buff)
 4923 tgl@sss.pgh.pa.us        1912                 :UBC           0 :         return NULL;
                               1913                 :                : 
 4923 tgl@sss.pgh.pa.us        1914                 :CBC        5725 :     result = pnstrdup(buff, nbytes);
                               1915                 :                : 
  303 peter@eisentraut.org     1916         [ +  + ]:          45267 :     for (char *p = result; *p; p++)
 4923 tgl@sss.pgh.pa.us        1917                 :          39542 :         *p = pg_ascii_toupper((unsigned char) *p);
                               1918                 :                : 
                               1919                 :           5725 :     return result;
                               1920                 :                : }
                               1921                 :                : 
                               1922                 :                : /*
                               1923                 :                :  * ASCII-only initcap function
                               1924                 :                :  *
                               1925                 :                :  * We pass the number of bytes so we can pass varlena and char*
                               1926                 :                :  * to this function.  The result is a palloc'd, null-terminated string.
                               1927                 :                :  */
                               1928                 :                : char *
                               1929                 :             16 : asc_initcap(const char *buff, size_t nbytes)
                               1930                 :                : {
                               1931                 :                :     char       *result;
                               1932                 :             16 :     int         wasalnum = false;
                               1933                 :                : 
                               1934         [ -  + ]:             16 :     if (!buff)
 4923 tgl@sss.pgh.pa.us        1935                 :UBC           0 :         return NULL;
                               1936                 :                : 
 4923 tgl@sss.pgh.pa.us        1937                 :CBC          16 :     result = pnstrdup(buff, nbytes);
                               1938                 :                : 
  303 peter@eisentraut.org     1939         [ +  + ]:             64 :     for (char *p = result; *p; p++)
                               1940                 :                :     {
                               1941                 :                :         char        c;
                               1942                 :                : 
 4923 tgl@sss.pgh.pa.us        1943         [ +  + ]:             48 :         if (wasalnum)
                               1944                 :             32 :             *p = c = pg_ascii_tolower((unsigned char) *p);
                               1945                 :                :         else
                               1946                 :             16 :             *p = c = pg_ascii_toupper((unsigned char) *p);
                               1947                 :                :         /* we don't trust isalnum() here */
                               1948   [ +  +  +  - ]:             96 :         wasalnum = ((c >= 'A' && c <= 'Z') ||
                               1949   [ +  -  -  +  :             96 :                     (c >= 'a' && c <= 'z') ||
                                              -  - ]
 4923 tgl@sss.pgh.pa.us        1950         [ #  # ]:UBC           0 :                     (c >= '0' && c <= '9'));
                               1951                 :                :     }
                               1952                 :                : 
 4923 tgl@sss.pgh.pa.us        1953                 :CBC          16 :     return result;
                               1954                 :                : }
                               1955                 :                : 
                               1956                 :                : /* convenience routines for when the input is null-terminated */
                               1957                 :                : 
                               1958                 :                : static char *
 5679 peter_e@gmx.net          1959                 :UBC           0 : str_tolower_z(const char *buff, Oid collid)
                               1960                 :                : {
                               1961                 :              0 :     return str_tolower(buff, strlen(buff), collid);
                               1962                 :                : }
                               1963                 :                : 
                               1964                 :                : static char *
                               1965                 :              0 : str_toupper_z(const char *buff, Oid collid)
                               1966                 :                : {
                               1967                 :              0 :     return str_toupper(buff, strlen(buff), collid);
                               1968                 :                : }
                               1969                 :                : 
                               1970                 :                : static char *
                               1971                 :              0 : str_initcap_z(const char *buff, Oid collid)
                               1972                 :                : {
                               1973                 :              0 :     return str_initcap(buff, strlen(buff), collid);
                               1974                 :                : }
                               1975                 :                : 
                               1976                 :                : static char *
 4923 tgl@sss.pgh.pa.us        1977                 :CBC        3072 : asc_tolower_z(const char *buff)
                               1978                 :                : {
                               1979                 :           3072 :     return asc_tolower(buff, strlen(buff));
                               1980                 :                : }
                               1981                 :                : 
                               1982                 :                : static char *
                               1983                 :           3048 : asc_toupper_z(const char *buff)
                               1984                 :                : {
                               1985                 :           3048 :     return asc_toupper(buff, strlen(buff));
                               1986                 :                : }
                               1987                 :                : 
                               1988                 :                : /* asc_initcap_z is not currently needed */
                               1989                 :                : 
                               1990                 :                : 
                               1991                 :                : /*
                               1992                 :                :  * Skip TM / th in FROM_CHAR
                               1993                 :                :  *
                               1994                 :                :  * If IS_SUFFIX_THth is on, skip two chars, assuming there are two available
                               1995                 :                :  */
                               1996                 :                : #define SKIP_THth(ptr, _suf) \
                               1997                 :                :     do { \
                               1998                 :                :         if (IS_SUFFIX_THth(_suf)) \
                               1999                 :                :         { \
                               2000                 :                :             if (*(ptr)) (ptr) += pg_mblen_cstr(ptr); \
                               2001                 :                :             if (*(ptr)) (ptr) += pg_mblen_cstr(ptr); \
                               2002                 :                :         } \
                               2003                 :                :     } while (0)
                               2004                 :                : 
                               2005                 :                : 
                               2006                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               2007                 :                : /*
                               2008                 :                :  * DEBUG: Call for debug and for index checking; (Show ASCII char
                               2009                 :                :  * and defined keyword for each used position
                               2010                 :                :  */
                               2011                 :                : static void
                               2012                 :                : dump_index(const KeyWord *k, const int *index)
                               2013                 :                : {
                               2014                 :                :     int         count = 0,
                               2015                 :                :                 free_i = 0;
                               2016                 :                : 
                               2017                 :                :     elog(DEBUG_elog_output, "TO-FROM_CHAR: Dump KeyWord Index:");
                               2018                 :                : 
                               2019                 :                :     for (int i = 0; i < KeyWord_INDEX_SIZE; i++)
                               2020                 :                :     {
                               2021                 :                :         if (index[i] != -1)
                               2022                 :                :         {
                               2023                 :                :             elog(DEBUG_elog_output, "\t%c: %s, ", i + 32, k[index[i]].name);
                               2024                 :                :             count++;
                               2025                 :                :         }
                               2026                 :                :         else
                               2027                 :                :         {
                               2028                 :                :             free_i++;
                               2029                 :                :             elog(DEBUG_elog_output, "\t(%d) %c %d", i, i + 32, index[i]);
                               2030                 :                :         }
                               2031                 :                :     }
                               2032                 :                :     elog(DEBUG_elog_output, "\n\t\tUsed positions: %d,\n\t\tFree positions: %d",
                               2033                 :                :          count, free_i);
                               2034                 :                : }
                               2035                 :                : #endif                          /* DEBUG */
                               2036                 :                : 
                               2037                 :                : /*
                               2038                 :                :  * Return true if next format picture is not digit value
                               2039                 :                :  */
                               2040                 :                : static bool
 9406 bruce@momjian.us         2041                 :          82567 : is_next_separator(FormatNode *n)
                               2042                 :                : {
                               2043         [ -  + ]:          82567 :     if (n->type == NODE_TYPE_END)
 3298 peter_e@gmx.net          2044                 :UBC           0 :         return false;
                               2045                 :                : 
  300 peter@eisentraut.org     2046   [ +  -  -  + ]:CBC       82567 :     if (n->type == NODE_TYPE_ACTION && IS_SUFFIX_THth(n->suffix))
 3298 peter_e@gmx.net          2047                 :UBC           0 :         return true;
                               2048                 :                : 
                               2049                 :                :     /*
                               2050                 :                :      * Next node
                               2051                 :                :      */
 9289 bruce@momjian.us         2052                 :CBC       82567 :     n++;
                               2053                 :                : 
                               2054                 :                :     /* end of format string is treated like a non-digit separator */
 9406                          2055         [ +  + ]:          82567 :     if (n->type == NODE_TYPE_END)
 3298 peter_e@gmx.net          2056                 :           8571 :         return true;
                               2057                 :                : 
 9406 bruce@momjian.us         2058         [ +  + ]:          73996 :     if (n->type == NODE_TYPE_ACTION)
                               2059                 :                :     {
 6732 tgl@sss.pgh.pa.us        2060         [ +  + ]:           4304 :         if (n->key->is_digit)
 3298 peter_e@gmx.net          2061                 :            320 :             return false;
                               2062                 :                : 
                               2063                 :           3984 :         return true;
                               2064                 :                :     }
 3204 tgl@sss.pgh.pa.us        2065         [ +  - ]:          69692 :     else if (n->character[1] == '\0' &&
                               2066         [ -  + ]:          69692 :              isdigit((unsigned char) n->character[0]))
 3298 peter_e@gmx.net          2067                 :UBC           0 :         return false;
                               2068                 :                : 
 3298 peter_e@gmx.net          2069                 :CBC       69692 :     return true;                /* some non-digit input (separator) */
                               2070                 :                : }
                               2071                 :                : 
                               2072                 :                : 
                               2073                 :                : static int
 5468 bruce@momjian.us         2074                 :             56 : adjust_partial_year_to_2020(int year)
                               2075                 :                : {
                               2076                 :                :     /*
                               2077                 :                :      * Adjust all dates toward 2020; this is effectively what happens when we
                               2078                 :                :      * assume '70' is 1970 and '69' is 2069.
                               2079                 :                :      */
                               2080                 :                :     /* Force 0-69 into the 2000's */
                               2081         [ +  + ]:             56 :     if (year < 70)
                               2082                 :             28 :         return year + 2000;
                               2083                 :                :     /* Force 70-99 into the 1900's */
 5169 tgl@sss.pgh.pa.us        2084         [ +  + ]:             28 :     else if (year < 100)
 5468 bruce@momjian.us         2085                 :             24 :         return year + 1900;
                               2086                 :                :     /* Force 100-519 into the 2000's */
 5169 tgl@sss.pgh.pa.us        2087         [ -  + ]:              4 :     else if (year < 520)
 5468 bruce@momjian.us         2088                 :UBC           0 :         return year + 2000;
                               2089                 :                :     /* Force 520-999 into the 1000's */
 5169 tgl@sss.pgh.pa.us        2090         [ +  - ]:CBC           4 :     else if (year < 1000)
 5468 bruce@momjian.us         2091                 :              4 :         return year + 1000;
                               2092                 :                :     else
 5468 bruce@momjian.us         2093                 :UBC           0 :         return year;
                               2094                 :                : }
                               2095                 :                : 
                               2096                 :                : 
                               2097                 :                : static size_t
 2408 tgl@sss.pgh.pa.us        2098                 :CBC       82595 : strspace_len(const char *str)
                               2099                 :                : {
  302 peter@eisentraut.org     2100                 :          82595 :     size_t      len = 0;
                               2101                 :                : 
 7435 bruce@momjian.us         2102   [ +  -  -  + ]:          82595 :     while (*str && isspace((unsigned char) *str))
                               2103                 :                :     {
 7435 bruce@momjian.us         2104                 :UBC           0 :         str++;
                               2105                 :              0 :         len++;
                               2106                 :                :     }
 7435 bruce@momjian.us         2107                 :CBC       82595 :     return len;
                               2108                 :                : }
                               2109                 :                : 
                               2110                 :                : /*
                               2111                 :                :  * Set the date mode of a from-char conversion.
                               2112                 :                :  *
                               2113                 :                :  * Puke if the date mode has already been set, and the caller attempts to set
                               2114                 :                :  * it to a conflicting mode.
                               2115                 :                :  *
                               2116                 :                :  * Returns true on success, false on failure (if escontext points to an
                               2117                 :                :  * ErrorSaveContext; otherwise errors are thrown).
                               2118                 :                :  */
                               2119                 :                : static bool
 1357 tgl@sss.pgh.pa.us        2120                 :          82577 : from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode,
                               2121                 :                :                    Node *escontext)
                               2122                 :                : {
 6559                          2123         [ +  + ]:          82577 :     if (mode != FROM_CHAR_DATE_NONE)
                               2124                 :                :     {
                               2125         [ +  + ]:          36643 :         if (tmfc->mode == FROM_CHAR_DATE_NONE)
                               2126                 :          13535 :             tmfc->mode = mode;
                               2127         [ +  + ]:          23108 :         else if (tmfc->mode != mode)
 1357                          2128         [ +  - ]:              4 :             ereturn(escontext, false,
                               2129                 :                :                     (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               2130                 :                :                      errmsg("invalid combination of date conventions"),
                               2131                 :                :                      errhint("Do not mix Gregorian and ISO week date conventions in a formatting template.")));
                               2132                 :                :     }
                               2133                 :          82573 :     return true;
                               2134                 :                : }
                               2135                 :                : 
                               2136                 :                : /*
                               2137                 :                :  * Set the integer pointed to by 'dest' to the given value.
                               2138                 :                :  *
                               2139                 :                :  * Puke if the destination integer has previously been set to some other
                               2140                 :                :  * non-zero value.
                               2141                 :                :  *
                               2142                 :                :  * Returns true on success, false on failure (if escontext points to an
                               2143                 :                :  * ErrorSaveContext; otherwise errors are thrown).
                               2144                 :                :  */
                               2145                 :                : static bool
 2528 akorotkov@postgresql     2146                 :          82174 : from_char_set_int(int *dest, const int value, const FormatNode *node,
                               2147                 :                :                   Node *escontext)
                               2148                 :                : {
 6559 tgl@sss.pgh.pa.us        2149   [ +  +  +  - ]:          82174 :     if (*dest != 0 && *dest != value)
 1357                          2150         [ +  - ]:              4 :         ereturn(escontext, false,
                               2151                 :                :                 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               2152                 :                :                  errmsg("conflicting values for \"%s\" field in formatting string",
                               2153                 :                :                         node->key->name),
                               2154                 :                :                  errdetail("This value contradicts a previous setting for the same field type.")));
 6559                          2155                 :          82170 :     *dest = value;
 1357                          2156                 :          82170 :     return true;
                               2157                 :                : }
                               2158                 :                : 
                               2159                 :                : /*
                               2160                 :                :  * Read a single integer from the source string, into the int pointed to by
                               2161                 :                :  * 'dest'. If 'dest' is NULL, the result is discarded.
                               2162                 :                :  *
                               2163                 :                :  * In fixed-width mode (the node does not have the FM suffix), consume at most
                               2164                 :                :  * 'len' characters.  However, any leading whitespace isn't counted in 'len'.
                               2165                 :                :  *
                               2166                 :                :  * We use strtol() to recover the integer value from the source string, in
                               2167                 :                :  * accordance with the given FormatNode.
                               2168                 :                :  *
                               2169                 :                :  * If the conversion completes successfully, src will have been advanced to
                               2170                 :                :  * point at the character immediately following the last character used in the
                               2171                 :                :  * conversion.
                               2172                 :                :  *
                               2173                 :                :  * Returns the number of characters consumed, or -1 on error (if escontext
                               2174                 :                :  * points to an ErrorSaveContext; otherwise errors are thrown).
                               2175                 :                :  *
                               2176                 :                :  * Note that from_char_parse_int() provides a more convenient wrapper where
                               2177                 :                :  * the length of the field is the same as the length of the format keyword (as
                               2178                 :                :  * with DD and MI).
                               2179                 :                :  */
                               2180                 :                : static int
  302 peter@eisentraut.org     2181                 :          82595 : from_char_parse_int_len(int *dest, const char **src, const size_t len, FormatNode *node,
                               2182                 :                :                         Node *escontext)
                               2183                 :                : {
                               2184                 :                :     long        result;
                               2185                 :                :     char        copy[16];
 2408 tgl@sss.pgh.pa.us        2186                 :          82595 :     const char *init = *src;
                               2187                 :                :     size_t      used;
                               2188                 :                : 
                               2189                 :                :     /*
                               2190                 :                :      * Skip any whitespace before parsing the integer.
                               2191                 :                :      */
 6275                          2192                 :          82595 :     *src += strspace_len(*src);
                               2193                 :                : 
                               2194                 :                :     /*
                               2195                 :                :      * Copy just the data to be parsed into copy[].  An Assert() is sufficient
                               2196                 :                :      * protection here because "len" is a constant property of the FormatNode
                               2197                 :                :      * and not dependent on the input string.
                               2198                 :                :      */
    7 tgl@sss.pgh.pa.us        2199         [ -  + ]:GNC       82595 :     Assert(len < sizeof(copy));
  302 peter@eisentraut.org     2200                 :CBC       82595 :     used = strlcpy(copy, *src, len + 1);
                               2201                 :                : 
  300                          2202   [ +  +  +  + ]:          82595 :     if (IS_SUFFIX_FM(node->suffix) || is_next_separator(node))
 6559 tgl@sss.pgh.pa.us        2203                 :          82275 :     {
                               2204                 :                :         /*
                               2205                 :                :          * This node is in Fill Mode, or the next node is known to be a
                               2206                 :                :          * non-digit value, so we just slurp as many characters as we can get.
                               2207                 :                :          */
                               2208                 :                :         char       *endptr;
                               2209                 :                : 
                               2210                 :          82275 :         errno = 0;
 2408                          2211                 :          82275 :         result = strtol(init, &endptr, 10);
                               2212                 :          82275 :         *src = endptr;
                               2213                 :                :     }
                               2214                 :                :     else
                               2215                 :                :     {
                               2216                 :                :         /*
                               2217                 :                :          * We need to pull exactly the number of characters given in 'len' out
                               2218                 :                :          * of the string, and convert those.
                               2219                 :                :          */
                               2220                 :                :         char       *last;
                               2221                 :                : 
 6410 bruce@momjian.us         2222         [ +  + ]:            320 :         if (used < len)
 1357 tgl@sss.pgh.pa.us        2223         [ +  - ]:              4 :             ereturn(escontext, -1,
                               2224                 :                :                     (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               2225                 :                :                      errmsg("source string too short for \"%s\" formatting field",
                               2226                 :                :                             node->key->name),
                               2227                 :                :                      errdetail("Field requires %zu characters, but only %zu remain.",
                               2228                 :                :                                len, used),
                               2229                 :                :                      errhint("If your source string is not fixed-width, try using the \"FM\" modifier.")));
                               2230                 :                : 
 6559                          2231                 :            316 :         errno = 0;
 6410 bruce@momjian.us         2232                 :            316 :         result = strtol(copy, &last, 10);
                               2233                 :            316 :         used = last - copy;
                               2234                 :                : 
 6559 tgl@sss.pgh.pa.us        2235   [ +  -  +  + ]:            316 :         if (used > 0 && used < len)
 1357                          2236         [ +  - ]:              4 :             ereturn(escontext, -1,
                               2237                 :                :                     (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               2238                 :                :                      errmsg("invalid value \"%s\" for \"%s\"",
                               2239                 :                :                             copy, node->key->name),
                               2240                 :                :                      errdetail("Field requires %zu characters, but only %zu could be parsed.",
                               2241                 :                :                                len, used),
                               2242                 :                :                      errhint("If your source string is not fixed-width, try using the \"FM\" modifier.")));
                               2243                 :                : 
 6559                          2244                 :            312 :         *src += used;
                               2245                 :                :     }
                               2246                 :                : 
                               2247         [ +  + ]:          82587 :     if (*src == init)
 1357                          2248         [ +  + ]:            557 :         ereturn(escontext, -1,
                               2249                 :                :                 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               2250                 :                :                  errmsg("invalid value \"%s\" for \"%s\"",
                               2251                 :                :                         copy, node->key->name),
                               2252                 :                :                  errdetail("Value must be an integer.")));
                               2253                 :                : 
 6559                          2254   [ +  -  +  -  :          82030 :     if (errno == ERANGE || result < INT_MIN || result > INT_MAX)
                                              +  + ]
 1357                          2255         [ +  - ]:              4 :         ereturn(escontext, -1,
                               2256                 :                :                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                               2257                 :                :                  errmsg("value for \"%s\" in source string is out of range",
                               2258                 :                :                         node->key->name),
                               2259                 :                :                  errdetail("Value must be in the range %d to %d.",
                               2260                 :                :                            INT_MIN, INT_MAX)));
                               2261                 :                : 
 6499 heikki.linnakangas@i     2262         [ +  + ]:          82026 :     if (dest != NULL)
                               2263                 :                :     {
 1357 tgl@sss.pgh.pa.us        2264         [ -  + ]:          82022 :         if (!from_char_set_int(dest, (int) result, node, escontext))
 1357 tgl@sss.pgh.pa.us        2265                 :UBC           0 :             return -1;
                               2266                 :                :     }
                               2267                 :                : 
 6559 tgl@sss.pgh.pa.us        2268                 :CBC       82026 :     return *src - init;
                               2269                 :                : }
                               2270                 :                : 
                               2271                 :                : /*
                               2272                 :                :  * Call from_char_parse_int_len(), using the length of the format keyword as
                               2273                 :                :  * the expected length of the field.
                               2274                 :                :  *
                               2275                 :                :  * Don't call this function if the field differs in length from the format
                               2276                 :                :  * keyword (as with HH24; the keyword length is 4, but the field length is 2).
                               2277                 :                :  * In such cases, call from_char_parse_int_len() instead to specify the
                               2278                 :                :  * required length explicitly.
                               2279                 :                :  */
                               2280                 :                : static int
 1357                          2281                 :          58623 : from_char_parse_int(int *dest, const char **src, FormatNode *node,
                               2282                 :                :                     Node *escontext)
                               2283                 :                : {
                               2284                 :          58623 :     return from_char_parse_int_len(dest, src, node->key->len, node, escontext);
                               2285                 :                : }
                               2286                 :                : 
                               2287                 :                : /*
                               2288                 :                :  * Sequentially search null-terminated "array" for a case-insensitive match
                               2289                 :                :  * to the initial character(s) of "name".
                               2290                 :                :  *
                               2291                 :                :  * Returns array index of match, or -1 for no match.
                               2292                 :                :  *
                               2293                 :                :  * *len is set to the length of the match, or 0 for no match.
                               2294                 :                :  *
                               2295                 :                :  * Case-insensitivity is defined per pg_ascii_tolower, so this is only
                               2296                 :                :  * suitable for comparisons to ASCII strings.
                               2297                 :                :  */
                               2298                 :                : static int
  302 peter@eisentraut.org     2299                 :            156 : seq_search_ascii(const char *name, const char *const *array, size_t *len)
                               2300                 :                : {
                               2301                 :                :     unsigned char firstc;
                               2302                 :                : 
 6559 tgl@sss.pgh.pa.us        2303                 :            156 :     *len = 0;
                               2304                 :                : 
                               2305                 :                :     /* empty string can't match anything */
                               2306         [ -  + ]:            156 :     if (!*name)
 6559 tgl@sss.pgh.pa.us        2307                 :UBC           0 :         return -1;
                               2308                 :                : 
                               2309                 :                :     /* we handle first char specially to gain some speed */
 2408 tgl@sss.pgh.pa.us        2310                 :CBC         156 :     firstc = pg_ascii_tolower((unsigned char) *name);
                               2311                 :                : 
  303 peter@eisentraut.org     2312         [ +  + ]:            664 :     for (const char *const *a = array; *a != NULL; a++)
                               2313                 :                :     {
                               2314                 :                :         /* compare first chars */
 2408 tgl@sss.pgh.pa.us        2315         [ +  + ]:            656 :         if (pg_ascii_tolower((unsigned char) **a) != firstc)
 6559                          2316                 :            476 :             continue;
                               2317                 :                : 
                               2318                 :                :         /* compare rest of string */
  303 peter@eisentraut.org     2319                 :            512 :         for (const char *p = *a + 1, *n = name + 1;; p++, n++)
                               2320                 :                :         {
                               2321                 :                :             /* return success if we matched whole array entry */
 6559 tgl@sss.pgh.pa.us        2322         [ +  + ]:            512 :             if (*p == '\0')
                               2323                 :                :             {
 2408                          2324                 :            148 :                 *len = n - name;
 6559                          2325                 :            148 :                 return a - array;
                               2326                 :                :             }
                               2327                 :                :             /* else, must have another character in "name" ... */
                               2328         [ -  + ]:            364 :             if (*n == '\0')
 6559 tgl@sss.pgh.pa.us        2329                 :UBC           0 :                 break;
                               2330                 :                :             /* ... and it must match */
 2408 tgl@sss.pgh.pa.us        2331         [ +  + ]:CBC         728 :             if (pg_ascii_tolower((unsigned char) *p) !=
                               2332                 :            364 :                 pg_ascii_tolower((unsigned char) *n))
 6559                          2333                 :             32 :                 break;
                               2334                 :                :         }
                               2335                 :                :     }
                               2336                 :                : 
                               2337                 :              8 :     return -1;
                               2338                 :                : }
                               2339                 :                : 
                               2340                 :                : /*
                               2341                 :                :  * Compare 'name' with 'element' in a case-insensitive way, by first
                               2342                 :                :  * converting 'name' to upper case, then lower case. ('element' is already
                               2343                 :                :  * case-folded that way.)
                               2344                 :                :  *
                               2345                 :                :  * A helper function for seq_search_localized().
                               2346                 :                :  */
                               2347                 :                : static bool
   22 alvherre@kurilemu.de     2348                 :UBC           0 : casefold_str_cmp(const char *name, size_t name_len,
                               2349                 :                :                  const char *element, size_t element_len,
                               2350                 :                :                  pg_locale_t mylocale)
                               2351                 :                : {
                               2352                 :                :     /*
                               2353                 :                :      * 'name' is expected to fit in MAX_L10N_DATA, even with the case
                               2354                 :                :      * conversions.
                               2355                 :                :      */
                               2356                 :                :     char        upper_substr[MAX_L10N_DATA];
                               2357                 :                :     size_t      upper_substr_len;
                               2358                 :                :     char        lower_substr[MAX_L10N_DATA];
                               2359                 :                :     size_t      lower_substr_len;
                               2360                 :                : 
                               2361                 :              0 :     upper_substr_len = pg_strupper(upper_substr, sizeof(upper_substr),
                               2362                 :                :                                    name, name_len,
                               2363                 :                :                                    mylocale);
                               2364         [ #  # ]:              0 :     if (upper_substr_len > sizeof(upper_substr) - 1)
                               2365                 :              0 :         return false;           /* shouldn't happen */
                               2366                 :              0 :     lower_substr_len = pg_strlower(lower_substr, sizeof(lower_substr),
                               2367                 :                :                                    upper_substr, upper_substr_len,
                               2368                 :                :                                    mylocale);
                               2369         [ #  # ]:              0 :     if (lower_substr_len > sizeof(lower_substr) - 1)
                               2370                 :              0 :         return false;           /* shouldn't happen */
                               2371                 :                : 
                               2372                 :              0 :     return strcmp(lower_substr, element) == 0;
                               2373                 :                : }
                               2374                 :                : 
                               2375                 :                : /*
                               2376                 :                :  * Sequentially search an array of possibly non-English words for
                               2377                 :                :  * a case-insensitive match to the initial character(s) of "name".
                               2378                 :                :  *
                               2379                 :                :  * This has the same API as seq_search_ascii(), but we use a more general
                               2380                 :                :  * case-folding transformation to achieve case-insensitivity.  Case folding
                               2381                 :                :  * is done per the rules of the collation identified by "collid".
                               2382                 :                :  *
                               2383                 :                :  * The array is treated as const, but we don't declare it that way because
                               2384                 :                :  * the arrays exported by pg_locale.c aren't const.
                               2385                 :                :  */
                               2386                 :                : static int
  302 peter@eisentraut.org     2387                 :              0 : seq_search_localized(const char *name, char **array, size_t *len, Oid collid)
                               2388                 :                : {
   22 alvherre@kurilemu.de     2389                 :              0 :     size_t      name_len = strlen(name);
                               2390                 :              0 :     const char *name_end = name + name_len;
                               2391                 :                :     char       *upper_name;
                               2392                 :                :     char       *lower_name;
                               2393                 :                :     pg_locale_t mylocale;
                               2394                 :                : 
 2368 tgl@sss.pgh.pa.us        2395                 :              0 :     *len = 0;
                               2396                 :                : 
                               2397                 :                :     /* empty string can't match anything */
                               2398         [ #  # ]:              0 :     if (!*name)
                               2399                 :              0 :         return -1;
                               2400                 :                : 
                               2401                 :                :     /*
                               2402                 :                :      * The case-folding processing done below is fairly expensive, so before
                               2403                 :                :      * doing that, make a quick pass to see if there is an exact match.
                               2404                 :                :      */
  303 peter@eisentraut.org     2405         [ #  # ]:              0 :     for (char **a = array; *a != NULL; a++)
                               2406                 :                :     {
  302                          2407                 :              0 :         size_t      element_len = strlen(*a);
                               2408                 :                : 
 2368 tgl@sss.pgh.pa.us        2409         [ #  # ]:              0 :         if (strncmp(name, *a, element_len) == 0)
                               2410                 :                :         {
                               2411                 :              0 :             *len = element_len;
                               2412                 :              0 :             return a - array;
                               2413                 :                :         }
                               2414                 :                :     }
                               2415                 :                : 
   22 alvherre@kurilemu.de     2416                 :              0 :     mylocale = pg_newlocale_from_collation(collid);
                               2417                 :                : 
                               2418                 :                :     /*
                               2419                 :                :      * Fold to upper case, then to lower case, so that we can match reliably
                               2420                 :                :      * even in languages in which case conversions are not injective.
                               2421                 :                :      */
                               2422                 :              0 :     upper_name = str_toupper(name, name_len, collid);
 2368 tgl@sss.pgh.pa.us        2423                 :              0 :     lower_name = str_tolower(upper_name, strlen(upper_name), collid);
                               2424                 :              0 :     pfree(upper_name);
                               2425                 :                : 
  303 peter@eisentraut.org     2426         [ #  # ]:              0 :     for (char **a = array; *a != NULL; a++)
                               2427                 :                :     {
                               2428                 :                :         char        upper_element[MAX_L10N_DATA];
                               2429                 :                :         size_t      upper_element_len;
                               2430                 :                :         char        lower_element[MAX_L10N_DATA];
                               2431                 :                :         size_t      lower_element_len;
                               2432                 :                : 
                               2433                 :                :         /* Likewise upper/lower-case array element */
   22 alvherre@kurilemu.de     2434                 :              0 :         upper_element_len = pg_strupper(upper_element, sizeof(upper_element),
                               2435                 :                :                                         *a, strlen(*a),
                               2436                 :                :                                         mylocale);
                               2437         [ #  # ]:              0 :         if (upper_element_len > sizeof(upper_element) - 1)
                               2438                 :              0 :             continue;           /* shouldn't happen */
                               2439                 :              0 :         lower_element_len = pg_strlower(lower_element, sizeof(lower_element),
                               2440                 :                :                                         upper_element, upper_element_len,
                               2441                 :                :                                         mylocale);
                               2442         [ #  # ]:              0 :         if (lower_element_len > sizeof(lower_element) - 1)
                               2443                 :              0 :             continue;           /* shouldn't happen */
                               2444                 :                : 
                               2445                 :                :         /* Is 'lower_element' a prefix of 'lower_name' ? */
                               2446         [ #  # ]:              0 :         if (strncmp(lower_name, lower_element, lower_element_len) == 0)
                               2447                 :                :         {
                               2448                 :                :             /*
                               2449                 :                :              * We have a match, but we still need to figure out how long the
                               2450                 :                :              * match is.  The case conversions could have changed the lengths
                               2451                 :                :              * of either string, or both.
                               2452                 :                :              */
                               2453                 :                :             const char *ep;
                               2454                 :                :             const char *element_end;
                               2455                 :                :             size_t      element_nchars;
                               2456                 :                :             size_t      substr_len;
                               2457                 :                :             size_t      substr_nchars;
                               2458                 :                : 
                               2459                 :                :             /*
                               2460                 :                :              * First, check the easy case that the string matches as whole.
                               2461                 :                :              */
                               2462         [ #  # ]:              0 :             if (strlen(lower_name) == lower_element_len)
                               2463                 :                :             {
                               2464                 :              0 :                 *len = name_len;
                               2465                 :              0 :                 pfree(lower_name);
                               2466                 :              0 :                 return a - array;
                               2467                 :                :             }
                               2468                 :                : 
                               2469                 :                :             /*
                               2470                 :                :              * Another good guess is that the case conversions did not change
                               2471                 :                :              * the number of characters.
                               2472                 :                :              */
                               2473                 :                : 
                               2474                 :                :             /* count characters in the element */
                               2475                 :              0 :             ep = lower_element;
                               2476                 :              0 :             element_end = lower_element + lower_element_len;
                               2477         [ #  # ]:              0 :             for (element_nchars = 0; ep < element_end; element_nchars++)
                               2478                 :              0 :                 ep += pg_mblen_range(ep, element_end);
                               2479                 :                : 
                               2480                 :                :             /*
                               2481                 :                :              * count the byte length of a substring of 'name' having the same
                               2482                 :                :              * character count as the element
                               2483                 :                :              */
                               2484                 :              0 :             substr_len = 0;
                               2485                 :              0 :             for (substr_nchars = 0;
                               2486   [ #  #  #  # ]:              0 :                  substr_nchars < element_nchars && substr_len < name_len;
                               2487                 :              0 :                  substr_nchars++)
                               2488                 :                :             {
                               2489                 :              0 :                 substr_len += pg_mblen_range(name + substr_len, name_end);
                               2490                 :                :             }
                               2491                 :                : 
                               2492         [ #  # ]:              0 :             if (casefold_str_cmp(name, substr_len, lower_element, lower_element_len, mylocale))
                               2493                 :                :             {
                               2494                 :              0 :                 *len = substr_len;
                               2495                 :              0 :                 pfree(lower_name);
                               2496                 :              0 :                 return a - array;
                               2497                 :                :             }
                               2498                 :                : 
                               2499                 :                :             /*
                               2500                 :                :              * As last resort, try the case conversion and comparison for
                               2501                 :                :              * every substring from the beginning of the original string until
                               2502                 :                :              * we find a match.
                               2503                 :                :              */
                               2504                 :              0 :             substr_len = 0;
                               2505         [ #  # ]:              0 :             while (substr_len < name_len)
                               2506                 :                :             {
                               2507                 :              0 :                 substr_len += pg_mblen_range(name + substr_len, name_end);
                               2508                 :                : 
                               2509         [ #  # ]:              0 :                 if (casefold_str_cmp(name, substr_len,
                               2510                 :                :                                      lower_element, lower_element_len,
                               2511                 :                :                                      mylocale))
                               2512                 :                :                 {
                               2513                 :              0 :                     *len = substr_len;
                               2514                 :              0 :                     pfree(lower_name);
                               2515                 :              0 :                     return a - array;
                               2516                 :                :                 }
                               2517                 :                :             }
                               2518                 :                :         }
                               2519                 :                :     }
                               2520                 :                : 
 2368 tgl@sss.pgh.pa.us        2521                 :              0 :     pfree(lower_name);
                               2522                 :              0 :     return -1;
                               2523                 :                : }
                               2524                 :                : 
                               2525                 :                : /*
                               2526                 :                :  * Perform a sequential search in 'array' (or 'localized_array', if that's
                               2527                 :                :  * not NULL) for an entry matching the first character(s) of the 'src'
                               2528                 :                :  * string case-insensitively.
                               2529                 :                :  *
                               2530                 :                :  * The 'array' is presumed to be English words (all-ASCII), but
                               2531                 :                :  * if 'localized_array' is supplied, that might be non-English
                               2532                 :                :  * so we need a more expensive case-folding transformation
                               2533                 :                :  * (which will follow the rules of the collation 'collid').
                               2534                 :                :  *
                               2535                 :                :  * If a match is found, copy the array index of the match into the integer
                               2536                 :                :  * pointed to by 'dest' and advance 'src' to the end of the part of the string
                               2537                 :                :  * which matched.
                               2538                 :                :  *
                               2539                 :                :  * Returns true on match, false on failure (if escontext points to an
                               2540                 :                :  * ErrorSaveContext; otherwise errors are thrown).
                               2541                 :                :  *
                               2542                 :                :  * 'node' is used only for error reports: node->key->name identifies the
                               2543                 :                :  * field type we were searching for.
                               2544                 :                :  */
                               2545                 :                : static bool
 2408 tgl@sss.pgh.pa.us        2546                 :CBC         156 : from_char_seq_search(int *dest, const char **src, const char *const *array,
                               2547                 :                :                      char **localized_array, Oid collid,
                               2548                 :                :                      FormatNode *node, Node *escontext)
                               2549                 :                : {
                               2550                 :                :     size_t      len;
                               2551                 :                : 
 2368                          2552         [ +  - ]:            156 :     if (localized_array == NULL)
                               2553                 :            156 :         *dest = seq_search_ascii(*src, array, &len);
                               2554                 :                :     else
 2368 tgl@sss.pgh.pa.us        2555                 :UBC           0 :         *dest = seq_search_localized(*src, localized_array, &len, collid);
                               2556                 :                : 
 6559 tgl@sss.pgh.pa.us        2557         [ +  + ]:CBC         156 :     if (len <= 0)
                               2558                 :                :     {
                               2559                 :                :         /*
                               2560                 :                :          * In the error report, truncate the string at the next whitespace (if
                               2561                 :                :          * any) to avoid including irrelevant data.
                               2562                 :                :          */
 2408                          2563                 :              8 :         char       *copy = pstrdup(*src);
                               2564                 :                : 
  303 peter@eisentraut.org     2565         [ +  + ]:             40 :         for (char *c = copy; *c; c++)
                               2566                 :                :         {
 2408 tgl@sss.pgh.pa.us        2567         [ +  + ]:             36 :             if (scanner_isspace(*c))
                               2568                 :                :             {
                               2569                 :              4 :                 *c = '\0';
                               2570                 :              4 :                 break;
                               2571                 :                :             }
                               2572                 :                :         }
                               2573                 :                : 
 1357                          2574         [ +  - ]:              8 :         ereturn(escontext, false,
                               2575                 :                :                 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               2576                 :                :                  errmsg("invalid value \"%s\" for \"%s\"",
                               2577                 :                :                         copy, node->key->name),
                               2578                 :                :                  errdetail("The given value did not match any of the allowed values for this field.")));
                               2579                 :                :     }
 6559                          2580                 :            148 :     *src += len;
 1357                          2581                 :            148 :     return true;
                               2582                 :                : }
                               2583                 :                : 
                               2584                 :                : /*
                               2585                 :                :  * Process a TmToChar struct as denoted by a list of FormatNodes.
                               2586                 :                :  * The formatted data is appended to 'out'.
                               2587                 :                :  */
                               2588                 :                : static void
    7 tgl@sss.pgh.pa.us        2589                 :GNC        6308 : DCH_to_char(const FormatNode *node, bool is_interval, Oid collid,
                               2590                 :                :             const TmToChar *in, StringInfo out)
                               2591                 :                : {
                               2592                 :           6308 :     const struct fmt_tm *tm = &in->tm;
                               2593                 :                :     int         i;
                               2594                 :                : 
                               2595                 :                : #define DCH_EMITF(...) appendStringInfo(out, __VA_ARGS__)
                               2596                 :                : #define DCH_EMITS(str) appendStringInfoString(out, str)
                               2597                 :                : #define DCH_EMITC(chr) appendStringInfoCharMacro(out, chr)
                               2598                 :                : 
                               2599                 :                :     /* cache localized days and months */
 6674 tgl@sss.pgh.pa.us        2600                 :CBC        6308 :     cache_locale_time();
                               2601                 :                : 
    7 tgl@sss.pgh.pa.us        2602         [ +  + ]:GNC      124390 :     for (const FormatNode *n = node; n->type != NODE_TYPE_END; n++)
                               2603                 :                :     {
                               2604                 :                :         int         field_start;
                               2605                 :                : 
 6732 tgl@sss.pgh.pa.us        2606         [ +  + ]:CBC      118082 :         if (n->type != NODE_TYPE_ACTION)
                               2607                 :                :         {
                               2608                 :                :             /* Optimize the common single-byte-string case */
    7 tgl@sss.pgh.pa.us        2609         [ +  - ]:GNC       69261 :             if (n->character[1] == '\0')
                               2610         [ -  + ]:          69261 :                 DCH_EMITC(n->character[0]);
                               2611                 :                :             else
    7 tgl@sss.pgh.pa.us        2612                 :UNC           0 :                 DCH_EMITS(n->character);
 6732 tgl@sss.pgh.pa.us        2613                 :CBC       69261 :             continue;
                               2614                 :                :         }
                               2615                 :                : 
                               2616                 :                :         /* Remember start of this field in case we need to call str_numth */
    7 tgl@sss.pgh.pa.us        2617                 :GNC       48821 :         field_start = out->len;
                               2618                 :                : 
 6732 tgl@sss.pgh.pa.us        2619   [ +  -  +  +  :CBC       48821 :         switch (n->key->id)
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  -  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                        +  +  -  +  
                                                 - ]
                               2620                 :                :         {
                               2621                 :            508 :             case DCH_A_M:
                               2622                 :                :             case DCH_P_M:
    7 tgl@sss.pgh.pa.us        2623         [ +  + ]:GNC         508 :                 DCH_EMITS((tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
                               2624                 :                :                           ? P_M_STR : A_M_STR);
 6732 tgl@sss.pgh.pa.us        2625                 :CBC         508 :                 break;
 6732 tgl@sss.pgh.pa.us        2626                 :UBC           0 :             case DCH_AM:
                               2627                 :                :             case DCH_PM:
    7 tgl@sss.pgh.pa.us        2628         [ #  # ]:UNC           0 :                 DCH_EMITS((tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
                               2629                 :                :                           ? PM_STR : AM_STR);
 6732 tgl@sss.pgh.pa.us        2630                 :UBC           0 :                 break;
 6732 tgl@sss.pgh.pa.us        2631                 :CBC         508 :             case DCH_a_m:
                               2632                 :                :             case DCH_p_m:
    7 tgl@sss.pgh.pa.us        2633         [ +  + ]:GNC         508 :                 DCH_EMITS((tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
                               2634                 :                :                           ? p_m_STR : a_m_STR);
 6732 tgl@sss.pgh.pa.us        2635                 :CBC         508 :                 break;
                               2636                 :            508 :             case DCH_am:
                               2637                 :                :             case DCH_pm:
    7 tgl@sss.pgh.pa.us        2638         [ +  + ]:GNC         508 :                 DCH_EMITS((tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
                               2639                 :                :                           ? pm_STR : am_STR);
 6732 tgl@sss.pgh.pa.us        2640                 :CBC         508 :                 break;
                               2641                 :           3066 :             case DCH_HH:
                               2642                 :                :             case DCH_HH12:
                               2643                 :                : 
                               2644                 :                :                 /*
                               2645                 :                :                  * display time as shown on a 12-hour clock, even for
                               2646                 :                :                  * intervals
                               2647                 :                :                  */
    7 tgl@sss.pgh.pa.us        2648   [ +  +  +  -  :GNC        3066 :                 DCH_EMITF("%0*lld", IS_SUFFIX_FM(n->suffix) ? 0 : (tm->tm_hour >= 0) ? 2 : 3,
                                              +  - ]
                               2649                 :                :                           tm->tm_hour % (HOURS_PER_DAY / 2) == 0 ?
                               2650                 :                :                           (long long) (HOURS_PER_DAY / 2) :
                               2651                 :                :                           (long long) (tm->tm_hour % (HOURS_PER_DAY / 2)));
  300 peter@eisentraut.org     2652         [ -  + ]:CBC        3066 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2653                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2654                 :CBC        3066 :                 break;
                               2655                 :           1017 :             case DCH_HH24:
    7 tgl@sss.pgh.pa.us        2656   [ +  -  +  - ]:GNC        1017 :                 DCH_EMITF("%0*lld", IS_SUFFIX_FM(n->suffix) ? 0 : (tm->tm_hour >= 0) ? 2 : 3,
                               2657                 :                :                           (long long) tm->tm_hour);
  300 peter@eisentraut.org     2658         [ -  + ]:CBC        1017 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2659                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2660                 :CBC        1017 :                 break;
                               2661                 :           3067 :             case DCH_MI:
    7 tgl@sss.pgh.pa.us        2662   [ +  -  +  - ]:GNC        3067 :                 DCH_EMITF("%0*d", IS_SUFFIX_FM(n->suffix) ? 0 : (tm->tm_min >= 0) ? 2 : 3,
                               2663                 :                :                           tm->tm_min);
  300 peter@eisentraut.org     2664         [ -  + ]:CBC        3067 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2665                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2666                 :CBC        3067 :                 break;
                               2667                 :           3067 :             case DCH_SS:
    7 tgl@sss.pgh.pa.us        2668   [ +  -  +  - ]:GNC        3067 :                 DCH_EMITF("%0*d", IS_SUFFIX_FM(n->suffix) ? 0 : (tm->tm_sec >= 0) ? 2 : 3,
                               2669                 :                :                           tm->tm_sec);
  300 peter@eisentraut.org     2670         [ -  + ]:CBC        3067 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2671                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2672                 :CBC        3067 :                 break;
                               2673                 :                : 
                               2674                 :                : #define DCH_to_char_fsec(frac_fmt, frac_val) \
                               2675                 :                :                 DCH_EMITF(frac_fmt, (int) (frac_val)); \
                               2676                 :                :                 if (IS_SUFFIX_THth(n->suffix)) \
                               2677                 :                :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
                               2678                 :                : 
 2537 akorotkov@postgresql     2679                 :             64 :             case DCH_FF1:       /* tenth of second */
                               2680         [ -  + ]:             64 :                 DCH_to_char_fsec("%01d", in->fsec / 100000);
                               2681                 :             64 :                 break;
                               2682                 :             64 :             case DCH_FF2:       /* hundredth of second */
                               2683         [ -  + ]:             64 :                 DCH_to_char_fsec("%02d", in->fsec / 10000);
                               2684                 :             64 :                 break;
                               2685                 :             96 :             case DCH_FF3:
                               2686                 :                :             case DCH_MS:        /* millisecond */
                               2687         [ -  + ]:             96 :                 DCH_to_char_fsec("%03d", in->fsec / 1000);
 6732 tgl@sss.pgh.pa.us        2688                 :             96 :                 break;
 2537 akorotkov@postgresql     2689                 :             64 :             case DCH_FF4:       /* tenth of a millisecond */
                               2690         [ -  + ]:             64 :                 DCH_to_char_fsec("%04d", in->fsec / 100);
                               2691                 :             64 :                 break;
                               2692                 :             64 :             case DCH_FF5:       /* hundredth of a millisecond */
                               2693         [ -  + ]:             64 :                 DCH_to_char_fsec("%05d", in->fsec / 10);
                               2694                 :             64 :                 break;
                               2695                 :             96 :             case DCH_FF6:
                               2696                 :                :             case DCH_US:        /* microsecond */
                               2697         [ -  + ]:             96 :                 DCH_to_char_fsec("%06d", in->fsec);
 6732 tgl@sss.pgh.pa.us        2698                 :             96 :                 break;
                               2699                 :                : #undef DCH_to_char_fsec
                               2700                 :            516 :             case DCH_SSSS:
    7 tgl@sss.pgh.pa.us        2701                 :GNC         516 :                 DCH_EMITF("%lld",
                               2702                 :                :                           (long long) (tm->tm_hour * SECS_PER_HOUR +
                               2703                 :                :                                        tm->tm_min * SECS_PER_MINUTE +
                               2704                 :                :                                        tm->tm_sec));
  300 peter@eisentraut.org     2705         [ -  + ]:CBC         516 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2706                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2707                 :CBC         516 :                 break;
                               2708                 :              4 :             case DCH_tz:
                               2709   [ -  +  -  - ]:              4 :                 INVALID_FOR_INTERVAL;
                               2710         [ +  - ]:              4 :                 if (tmtcTzn(in))
                               2711                 :                :                 {
                               2712                 :                :                     /*
                               2713                 :                :                      * We assume here that timezone abbreviations aren't
                               2714                 :                :                      * localized, so ASCII-only downcasing is sufficient.
                               2715                 :                :                      */
 4923                          2716                 :              4 :                     char       *p = asc_tolower_z(tmtcTzn(in));
                               2717                 :                : 
    7 tgl@sss.pgh.pa.us        2718                 :GNC           4 :                     DCH_EMITS(p);
 6732 tgl@sss.pgh.pa.us        2719                 :CBC           4 :                     pfree(p);
                               2720                 :                :                 }
                               2721                 :              4 :                 break;
                               2722                 :             12 :             case DCH_TZ:
                               2723   [ -  +  -  - ]:             12 :                 INVALID_FOR_INTERVAL;
                               2724         [ +  - ]:             12 :                 if (tmtcTzn(in))
    7 tgl@sss.pgh.pa.us        2725                 :GNC          12 :                     DCH_EMITS(tmtcTzn(in));
 6732 tgl@sss.pgh.pa.us        2726                 :CBC          12 :                 break;
 3152 andrew@dunslane.net      2727                 :             72 :             case DCH_TZH:
                               2728   [ -  +  -  - ]:             72 :                 INVALID_FOR_INTERVAL;
    7 tgl@sss.pgh.pa.us        2729         [ +  + ]:GNC          72 :                 DCH_EMITF("%c%02d",
                               2730                 :                :                           (tm->tm_gmtoff >= 0) ? '+' : '-',
                               2731                 :                :                           abs((int) tm->tm_gmtoff) / SECS_PER_HOUR);
 3152 andrew@dunslane.net      2732                 :CBC          72 :                 break;
                               2733                 :             72 :             case DCH_TZM:
                               2734   [ -  +  -  - ]:             72 :                 INVALID_FOR_INTERVAL;
    7 tgl@sss.pgh.pa.us        2735                 :GNC          72 :                 DCH_EMITF("%02d",
                               2736                 :                :                           (abs((int) tm->tm_gmtoff) % SECS_PER_HOUR) / SECS_PER_MINUTE);
 3152 andrew@dunslane.net      2737                 :CBC          72 :                 break;
 4805 bruce@momjian.us         2738                 :             72 :             case DCH_OF:
                               2739   [ -  +  -  - ]:             72 :                 INVALID_FOR_INTERVAL;
    7 tgl@sss.pgh.pa.us        2740   [ -  +  +  + ]:GNC          72 :                 DCH_EMITF("%c%0*d",
                               2741                 :                :                           (tm->tm_gmtoff >= 0) ? '+' : '-',
                               2742                 :                :                           IS_SUFFIX_FM(n->suffix) ? 0 : 2,
                               2743                 :                :                           abs((int) tm->tm_gmtoff) / SECS_PER_HOUR);
 3815 tgl@sss.pgh.pa.us        2744         [ +  + ]:CBC          72 :                 if (abs((int) tm->tm_gmtoff) % SECS_PER_HOUR != 0)
    7 tgl@sss.pgh.pa.us        2745                 :GNC          48 :                     DCH_EMITF(":%02d",
                               2746                 :                :                               (abs((int) tm->tm_gmtoff) % SECS_PER_HOUR) / SECS_PER_MINUTE);
 4805 bruce@momjian.us         2747                 :CBC          72 :                 break;
 6732 tgl@sss.pgh.pa.us        2748                 :            508 :             case DCH_A_D:
                               2749                 :                :             case DCH_B_C:
                               2750   [ -  +  -  - ]:            508 :                 INVALID_FOR_INTERVAL;
    7 tgl@sss.pgh.pa.us        2751         [ +  + ]:GNC         508 :                 DCH_EMITS((tm->tm_year <= 0 ? B_C_STR : A_D_STR));
 6732 tgl@sss.pgh.pa.us        2752                 :CBC         508 :                 break;
 6732 tgl@sss.pgh.pa.us        2753                 :UBC           0 :             case DCH_AD:
                               2754                 :                :             case DCH_BC:
                               2755   [ #  #  #  # ]:              0 :                 INVALID_FOR_INTERVAL;
    7 tgl@sss.pgh.pa.us        2756         [ #  # ]:UNC           0 :                 DCH_EMITS((tm->tm_year <= 0 ? BC_STR : AD_STR));
 6732 tgl@sss.pgh.pa.us        2757                 :UBC           0 :                 break;
 6732 tgl@sss.pgh.pa.us        2758                 :CBC         508 :             case DCH_a_d:
                               2759                 :                :             case DCH_b_c:
                               2760   [ -  +  -  - ]:            508 :                 INVALID_FOR_INTERVAL;
    7 tgl@sss.pgh.pa.us        2761         [ +  + ]:GNC         508 :                 DCH_EMITS((tm->tm_year <= 0 ? b_c_STR : a_d_STR));
 6732 tgl@sss.pgh.pa.us        2762                 :CBC         508 :                 break;
                               2763                 :            508 :             case DCH_ad:
                               2764                 :                :             case DCH_bc:
                               2765   [ -  +  -  - ]:            508 :                 INVALID_FOR_INTERVAL;
    7 tgl@sss.pgh.pa.us        2766         [ +  + ]:GNC         508 :                 DCH_EMITS((tm->tm_year <= 0 ? bc_STR : ad_STR));
 6732 tgl@sss.pgh.pa.us        2767                 :CBC         508 :                 break;
                               2768                 :           1016 :             case DCH_MONTH:
                               2769   [ -  +  -  - ]:           1016 :                 INVALID_FOR_INTERVAL;
                               2770         [ -  + ]:           1016 :                 if (!tm->tm_mon)
 6732 tgl@sss.pgh.pa.us        2771                 :UBC           0 :                     break;
  300 peter@eisentraut.org     2772         [ -  + ]:CBC        1016 :                 if (IS_SUFFIX_TM(n->suffix))
    7 tgl@sss.pgh.pa.us        2773                 :UNC           0 :                     DCH_EMITS(str_toupper_z(localized_full_months[tm->tm_mon - 1], collid));
                               2774                 :                :                 else
    7 tgl@sss.pgh.pa.us        2775         [ +  + ]:GNC        1016 :                     DCH_EMITF("%*s", IS_SUFFIX_FM(n->suffix) ? 0 : -9,
                               2776                 :                :                               asc_toupper_z(months_full[tm->tm_mon - 1]));
 6732 tgl@sss.pgh.pa.us        2777                 :CBC        1016 :                 break;
                               2778                 :           1016 :             case DCH_Month:
                               2779   [ -  +  -  - ]:           1016 :                 INVALID_FOR_INTERVAL;
                               2780         [ -  + ]:           1016 :                 if (!tm->tm_mon)
 6732 tgl@sss.pgh.pa.us        2781                 :UBC           0 :                     break;
  300 peter@eisentraut.org     2782         [ -  + ]:CBC        1016 :                 if (IS_SUFFIX_TM(n->suffix))
    7 tgl@sss.pgh.pa.us        2783                 :UNC           0 :                     DCH_EMITS(str_initcap_z(localized_full_months[tm->tm_mon - 1], collid));
                               2784                 :                :                 else
    7 tgl@sss.pgh.pa.us        2785         [ +  + ]:GNC        1016 :                     DCH_EMITF("%*s", IS_SUFFIX_FM(n->suffix) ? 0 : -9,
                               2786                 :                :                               months_full[tm->tm_mon - 1]);
 6732 tgl@sss.pgh.pa.us        2787                 :CBC        1016 :                 break;
                               2788                 :           1016 :             case DCH_month:
                               2789   [ -  +  -  - ]:           1016 :                 INVALID_FOR_INTERVAL;
                               2790         [ -  + ]:           1016 :                 if (!tm->tm_mon)
 6732 tgl@sss.pgh.pa.us        2791                 :UBC           0 :                     break;
  300 peter@eisentraut.org     2792         [ -  + ]:CBC        1016 :                 if (IS_SUFFIX_TM(n->suffix))
    7 tgl@sss.pgh.pa.us        2793                 :UNC           0 :                     DCH_EMITS(str_tolower_z(localized_full_months[tm->tm_mon - 1], collid));
                               2794                 :                :                 else
    7 tgl@sss.pgh.pa.us        2795         [ +  + ]:GNC        1016 :                     DCH_EMITF("%*s", IS_SUFFIX_FM(n->suffix) ? 0 : -9,
                               2796                 :                :                               asc_tolower_z(months_full[tm->tm_mon - 1]));
 6732 tgl@sss.pgh.pa.us        2797                 :CBC        1016 :                 break;
                               2798                 :            508 :             case DCH_MON:
                               2799   [ -  +  -  - ]:            508 :                 INVALID_FOR_INTERVAL;
                               2800         [ -  + ]:            508 :                 if (!tm->tm_mon)
 6732 tgl@sss.pgh.pa.us        2801                 :UBC           0 :                     break;
  300 peter@eisentraut.org     2802         [ -  + ]:CBC         508 :                 if (IS_SUFFIX_TM(n->suffix))
    7 tgl@sss.pgh.pa.us        2803                 :UNC           0 :                     DCH_EMITS(str_toupper_z(localized_abbrev_months[tm->tm_mon - 1], collid));
                               2804                 :                :                 else
    7 tgl@sss.pgh.pa.us        2805                 :GNC         508 :                     DCH_EMITS(asc_toupper_z(months[tm->tm_mon - 1]));
 6732 tgl@sss.pgh.pa.us        2806                 :CBC         508 :                 break;
                               2807                 :            564 :             case DCH_Mon:
                               2808   [ -  +  -  - ]:            564 :                 INVALID_FOR_INTERVAL;
                               2809         [ -  + ]:            564 :                 if (!tm->tm_mon)
 6732 tgl@sss.pgh.pa.us        2810                 :UBC           0 :                     break;
  300 peter@eisentraut.org     2811         [ -  + ]:CBC         564 :                 if (IS_SUFFIX_TM(n->suffix))
    7 tgl@sss.pgh.pa.us        2812                 :UNC           0 :                     DCH_EMITS(str_initcap_z(localized_abbrev_months[tm->tm_mon - 1], collid));
                               2813                 :                :                 else
    7 tgl@sss.pgh.pa.us        2814                 :GNC         564 :                     DCH_EMITS(months[tm->tm_mon - 1]);
 6732 tgl@sss.pgh.pa.us        2815                 :CBC         564 :                 break;
                               2816                 :            508 :             case DCH_mon:
                               2817   [ -  +  -  - ]:            508 :                 INVALID_FOR_INTERVAL;
                               2818         [ -  + ]:            508 :                 if (!tm->tm_mon)
 6732 tgl@sss.pgh.pa.us        2819                 :UBC           0 :                     break;
  300 peter@eisentraut.org     2820         [ -  + ]:CBC         508 :                 if (IS_SUFFIX_TM(n->suffix))
    7 tgl@sss.pgh.pa.us        2821                 :UNC           0 :                     DCH_EMITS(str_tolower_z(localized_abbrev_months[tm->tm_mon - 1], collid));
                               2822                 :                :                 else
    7 tgl@sss.pgh.pa.us        2823                 :GNC         508 :                     DCH_EMITS(asc_tolower_z(months[tm->tm_mon - 1]));
 6732 tgl@sss.pgh.pa.us        2824                 :CBC         508 :                 break;
                               2825                 :           1284 :             case DCH_MM:
    7 tgl@sss.pgh.pa.us        2826   [ +  +  +  - ]:GNC        1284 :                 DCH_EMITF("%0*d", IS_SUFFIX_FM(n->suffix) ? 0 : (tm->tm_mon >= 0) ? 2 : 3,
                               2827                 :                :                           tm->tm_mon);
  300 peter@eisentraut.org     2828         [ -  + ]:CBC        1284 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2829                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2830                 :CBC        1284 :                 break;
                               2831                 :           1016 :             case DCH_DAY:
                               2832   [ -  +  -  - ]:           1016 :                 INVALID_FOR_INTERVAL;
  300 peter@eisentraut.org     2833         [ -  + ]:           1016 :                 if (IS_SUFFIX_TM(n->suffix))
    7 tgl@sss.pgh.pa.us        2834                 :UNC           0 :                     DCH_EMITS(str_toupper_z(localized_full_days[tm->tm_wday], collid));
                               2835                 :                :                 else
    7 tgl@sss.pgh.pa.us        2836         [ +  + ]:GNC        1016 :                     DCH_EMITF("%*s", IS_SUFFIX_FM(n->suffix) ? 0 : -9,
                               2837                 :                :                               asc_toupper_z(days[tm->tm_wday]));
 6732 tgl@sss.pgh.pa.us        2838                 :CBC        1016 :                 break;
                               2839                 :           1016 :             case DCH_Day:
                               2840   [ -  +  -  - ]:           1016 :                 INVALID_FOR_INTERVAL;
  300 peter@eisentraut.org     2841         [ -  + ]:           1016 :                 if (IS_SUFFIX_TM(n->suffix))
    7 tgl@sss.pgh.pa.us        2842                 :UNC           0 :                     DCH_EMITS(str_initcap_z(localized_full_days[tm->tm_wday], collid));
                               2843                 :                :                 else
    7 tgl@sss.pgh.pa.us        2844         [ +  + ]:GNC        1016 :                     DCH_EMITF("%*s", IS_SUFFIX_FM(n->suffix) ? 0 : -9,
                               2845                 :                :                               days[tm->tm_wday]);
 6732 tgl@sss.pgh.pa.us        2846                 :CBC        1016 :                 break;
                               2847                 :           1016 :             case DCH_day:
                               2848   [ -  +  -  - ]:           1016 :                 INVALID_FOR_INTERVAL;
  300 peter@eisentraut.org     2849         [ -  + ]:           1016 :                 if (IS_SUFFIX_TM(n->suffix))
    7 tgl@sss.pgh.pa.us        2850                 :UNC           0 :                     DCH_EMITS(str_tolower_z(localized_full_days[tm->tm_wday], collid));
                               2851                 :                :                 else
    7 tgl@sss.pgh.pa.us        2852         [ +  + ]:GNC        1016 :                     DCH_EMITF("%*s", IS_SUFFIX_FM(n->suffix) ? 0 : -9,
                               2853                 :                :                               asc_tolower_z(days[tm->tm_wday]));
 6732 tgl@sss.pgh.pa.us        2854                 :CBC        1016 :                 break;
                               2855                 :            508 :             case DCH_DY:
                               2856   [ -  +  -  - ]:            508 :                 INVALID_FOR_INTERVAL;
  300 peter@eisentraut.org     2857         [ -  + ]:            508 :                 if (IS_SUFFIX_TM(n->suffix))
    7 tgl@sss.pgh.pa.us        2858                 :UNC           0 :                     DCH_EMITS(str_toupper_z(localized_abbrev_days[tm->tm_wday], collid));
                               2859                 :                :                 else
    7 tgl@sss.pgh.pa.us        2860                 :GNC         508 :                     DCH_EMITS(asc_toupper_z(days_short[tm->tm_wday]));
 6732 tgl@sss.pgh.pa.us        2861                 :CBC         508 :                 break;
                               2862                 :            508 :             case DCH_Dy:
                               2863   [ -  +  -  - ]:            508 :                 INVALID_FOR_INTERVAL;
  300 peter@eisentraut.org     2864         [ -  + ]:            508 :                 if (IS_SUFFIX_TM(n->suffix))
    7 tgl@sss.pgh.pa.us        2865                 :UNC           0 :                     DCH_EMITS(str_initcap_z(localized_abbrev_days[tm->tm_wday], collid));
                               2866                 :                :                 else
    7 tgl@sss.pgh.pa.us        2867                 :GNC         508 :                     DCH_EMITS(days_short[tm->tm_wday]);
 6732 tgl@sss.pgh.pa.us        2868                 :CBC         508 :                 break;
                               2869                 :            508 :             case DCH_dy:
                               2870   [ -  +  -  - ]:            508 :                 INVALID_FOR_INTERVAL;
  300 peter@eisentraut.org     2871         [ -  + ]:            508 :                 if (IS_SUFFIX_TM(n->suffix))
    7 tgl@sss.pgh.pa.us        2872                 :UNC           0 :                     DCH_EMITS(str_tolower_z(localized_abbrev_days[tm->tm_wday], collid));
                               2873                 :                :                 else
    7 tgl@sss.pgh.pa.us        2874                 :GNC         508 :                     DCH_EMITS(asc_tolower_z(days_short[tm->tm_wday]));
 6732 tgl@sss.pgh.pa.us        2875                 :CBC         508 :                 break;
                               2876                 :           2032 :             case DCH_DDD:
                               2877                 :                :             case DCH_IDDD:
    7 tgl@sss.pgh.pa.us        2878   [ +  +  +  + ]:GNC        2032 :                 DCH_EMITF("%0*d", IS_SUFFIX_FM(n->suffix) ? 0 : 3,
                               2879                 :                :                           (n->key->id == DCH_DDD) ?
                               2880                 :                :                           tm->tm_yday :
                               2881                 :                :                           date2isoyearday(tm->tm_year, tm->tm_mon, tm->tm_mday));
  300 peter@eisentraut.org     2882         [ -  + ]:CBC        2032 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2883                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2884                 :CBC        2032 :                 break;
                               2885                 :           1040 :             case DCH_DD:
    7 tgl@sss.pgh.pa.us        2886         [ +  + ]:GNC        1040 :                 DCH_EMITF("%0*d", IS_SUFFIX_FM(n->suffix) ? 0 : 2, tm->tm_mday);
  300 peter@eisentraut.org     2887         [ -  + ]:CBC        1040 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2888                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2889                 :CBC        1040 :                 break;
                               2890                 :           1016 :             case DCH_D:
                               2891   [ -  +  -  - ]:           1016 :                 INVALID_FOR_INTERVAL;
    7 tgl@sss.pgh.pa.us        2892                 :GNC        1016 :                 DCH_EMITF("%d", tm->tm_wday + 1);
  300 peter@eisentraut.org     2893         [ -  + ]:CBC        1016 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2894                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2895                 :CBC        1016 :                 break;
                               2896                 :           1016 :             case DCH_ID:
                               2897   [ -  +  -  - ]:           1016 :                 INVALID_FOR_INTERVAL;
    7 tgl@sss.pgh.pa.us        2898         [ +  + ]:GNC        1016 :                 DCH_EMITF("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
  300 peter@eisentraut.org     2899         [ -  + ]:CBC        1016 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2900                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2901                 :CBC        1016 :                 break;
                               2902                 :           1016 :             case DCH_WW:
    7 tgl@sss.pgh.pa.us        2903         [ +  + ]:GNC        1016 :                 DCH_EMITF("%0*d", IS_SUFFIX_FM(n->suffix) ? 0 : 2,
                               2904                 :                :                           (tm->tm_yday - 1) / 7 + 1);
  300 peter@eisentraut.org     2905         [ -  + ]:CBC        1016 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2906                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2907                 :CBC        1016 :                 break;
                               2908                 :           1016 :             case DCH_IW:
    7 tgl@sss.pgh.pa.us        2909         [ +  + ]:GNC        1016 :                 DCH_EMITF("%0*d", IS_SUFFIX_FM(n->suffix) ? 0 : 2,
                               2910                 :                :                           date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday));
  300 peter@eisentraut.org     2911         [ -  + ]:CBC        1016 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2912                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2913                 :CBC        1016 :                 break;
                               2914                 :           1016 :             case DCH_Q:
                               2915         [ -  + ]:           1016 :                 if (!tm->tm_mon)
 6732 tgl@sss.pgh.pa.us        2916                 :UBC           0 :                     break;
    7 tgl@sss.pgh.pa.us        2917                 :GNC        1016 :                 DCH_EMITF("%d", (tm->tm_mon - 1) / 3 + 1);
  300 peter@eisentraut.org     2918         [ -  + ]:CBC        1016 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2919                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2920                 :CBC        1016 :                 break;
                               2921                 :           1016 :             case DCH_CC:
 6286 bruce@momjian.us         2922         [ -  + ]:           1016 :                 if (is_interval)    /* straight calculation */
 6732 tgl@sss.pgh.pa.us        2923                 :UBC           0 :                     i = tm->tm_year / 100;
                               2924                 :                :                 else
                               2925                 :                :                 {
 5133 bruce@momjian.us         2926         [ +  + ]:CBC        1016 :                     if (tm->tm_year > 0)
                               2927                 :                :                         /* Century 20 == 1901 - 2000 */
                               2928                 :           1000 :                         i = (tm->tm_year - 1) / 100 + 1;
                               2929                 :                :                     else
                               2930                 :                :                         /* Century 6BC == 600BC - 501BC */
                               2931                 :             16 :                         i = tm->tm_year / 100 - 1;
                               2932                 :                :                 }
 6732 tgl@sss.pgh.pa.us        2933   [ +  -  +  - ]:           1016 :                 if (i <= 99 && i >= -99)
    7 tgl@sss.pgh.pa.us        2934   [ +  +  +  + ]:GNC        1016 :                     DCH_EMITF("%0*d", IS_SUFFIX_FM(n->suffix) ? 0 : (i >= 0) ? 2 : 3, i);
                               2935                 :                :                 else
    7 tgl@sss.pgh.pa.us        2936                 :UNC           0 :                     DCH_EMITF("%d", i);
  300 peter@eisentraut.org     2937         [ -  + ]:CBC        1016 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2938                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2939                 :CBC        1016 :                 break;
                               2940                 :           1016 :             case DCH_Y_YYY:
                               2941   [ -  +  +  + ]:           1016 :                 i = ADJUST_YEAR(tm->tm_year, is_interval) / 1000;
    7 tgl@sss.pgh.pa.us        2942   [ -  +  +  + ]:GNC        1016 :                 DCH_EMITF("%d,%03d", i,
                               2943                 :                :                           ADJUST_YEAR(tm->tm_year, is_interval) - (i * 1000));
  300 peter@eisentraut.org     2944         [ -  + ]:CBC        1016 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2945                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2946                 :CBC        1016 :                 break;
                               2947                 :           4840 :             case DCH_YYYY:
                               2948                 :                :             case DCH_IYYY:
    7 tgl@sss.pgh.pa.us        2949   [ +  +  -  +  :GNC        4840 :                 DCH_EMITF("%0*d",
                                     +  +  -  +  +  
                                     +  +  +  -  +  
                                        +  +  +  - ]
                               2950                 :                :                           IS_SUFFIX_FM(n->suffix) ? 0 :
                               2951                 :                :                           (ADJUST_YEAR(tm->tm_year, is_interval) >= 0) ? 4 : 5,
                               2952                 :                :                           (n->key->id == DCH_YYYY ?
                               2953                 :                :                            ADJUST_YEAR(tm->tm_year, is_interval) :
                               2954                 :                :                            ADJUST_YEAR(date2isoyear(tm->tm_year,
                               2955                 :                :                                                     tm->tm_mon,
                               2956                 :                :                                                     tm->tm_mday),
                               2957                 :                :                                        is_interval)));
  300 peter@eisentraut.org     2958         [ +  + ]:CBC        4840 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2959                 :GNC        1016 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2960                 :CBC        4840 :                 break;
                               2961                 :           2032 :             case DCH_YYY:
                               2962                 :                :             case DCH_IYY:
    7 tgl@sss.pgh.pa.us        2963   [ +  +  -  +  :GNC        2032 :                 DCH_EMITF("%0*d",
                                     +  +  -  +  +  
                                     +  +  +  -  +  
                                        +  +  +  - ]
                               2964                 :                :                           IS_SUFFIX_FM(n->suffix) ? 0 :
                               2965                 :                :                           (ADJUST_YEAR(tm->tm_year, is_interval) >= 0) ? 3 : 4,
                               2966                 :                :                           (n->key->id == DCH_YYY ?
                               2967                 :                :                            ADJUST_YEAR(tm->tm_year, is_interval) :
                               2968                 :                :                            ADJUST_YEAR(date2isoyear(tm->tm_year,
                               2969                 :                :                                                     tm->tm_mon,
                               2970                 :                :                                                     tm->tm_mday),
                               2971                 :                :                                        is_interval)) % 1000);
  300 peter@eisentraut.org     2972         [ -  + ]:CBC        2032 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2973                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2974                 :CBC        2032 :                 break;
                               2975                 :           2032 :             case DCH_YY:
                               2976                 :                :             case DCH_IY:
    7 tgl@sss.pgh.pa.us        2977   [ +  +  -  +  :GNC        2032 :                 DCH_EMITF("%0*d",
                                     +  +  -  +  +  
                                     +  +  +  -  +  
                                        +  +  +  - ]
                               2978                 :                :                           IS_SUFFIX_FM(n->suffix) ? 0 :
                               2979                 :                :                           (ADJUST_YEAR(tm->tm_year, is_interval) >= 0) ? 2 : 3,
                               2980                 :                :                           (n->key->id == DCH_YY ?
                               2981                 :                :                            ADJUST_YEAR(tm->tm_year, is_interval) :
                               2982                 :                :                            ADJUST_YEAR(date2isoyear(tm->tm_year,
                               2983                 :                :                                                     tm->tm_mon,
                               2984                 :                :                                                     tm->tm_mday),
                               2985                 :                :                                        is_interval)) % 100);
  300 peter@eisentraut.org     2986         [ -  + ]:CBC        2032 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2987                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        2988                 :CBC        2032 :                 break;
                               2989                 :           2032 :             case DCH_Y:
                               2990                 :                :             case DCH_I:
    7 tgl@sss.pgh.pa.us        2991   [ +  +  -  +  :GNC        2032 :                 DCH_EMITF("%1d",
                                     +  +  -  +  +  
                                                 + ]
                               2992                 :                :                           (n->key->id == DCH_Y ?
                               2993                 :                :                            ADJUST_YEAR(tm->tm_year, is_interval) :
                               2994                 :                :                            ADJUST_YEAR(date2isoyear(tm->tm_year,
                               2995                 :                :                                                     tm->tm_mon,
                               2996                 :                :                                                     tm->tm_mday),
                               2997                 :                :                                        is_interval)) % 10);
  300 peter@eisentraut.org     2998         [ -  + ]:CBC        2032 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        2999                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        3000                 :CBC        2032 :                 break;
                               3001                 :           1232 :             case DCH_RM:
                               3002                 :                :                 pg_fallthrough;
                               3003                 :                :             case DCH_rm:
                               3004                 :                : 
                               3005                 :                :                 /*
                               3006                 :                :                  * For intervals, values like '12 month' will be reduced to 0
                               3007                 :                :                  * month and some years.  These should be processed.
                               3008                 :                :                  */
 1963 michael@paquier.xyz      3009   [ +  +  +  + ]:           1232 :                 if (!tm->tm_mon && !tm->tm_year)
                               3010                 :                :                     break;
                               3011                 :                :                 else
                               3012                 :                :                 {
                               3013                 :           1224 :                     int         mon = 0;
                               3014                 :                :                     const char *const *months;
                               3015                 :                : 
                               3016         [ +  + ]:           1224 :                     if (n->key->id == DCH_RM)
                               3017                 :           1120 :                         months = rm_months_upper;
                               3018                 :                :                     else
                               3019                 :            104 :                         months = rm_months_lower;
                               3020                 :                : 
                               3021                 :                :                     /*
                               3022                 :                :                      * Compute the position in the roman-numeral array.  Note
                               3023                 :                :                      * that the contents of the array are reversed, December
                               3024                 :                :                      * being first and January last.
                               3025                 :                :                      */
                               3026         [ +  + ]:           1224 :                     if (tm->tm_mon == 0)
                               3027                 :                :                     {
                               3028                 :                :                         /*
                               3029                 :                :                          * This case is special, and tracks the case of full
                               3030                 :                :                          * interval years.
                               3031                 :                :                          */
                               3032         [ +  + ]:             16 :                         mon = tm->tm_year >= 0 ? 0 : MONTHS_PER_YEAR - 1;
                               3033                 :                :                     }
                               3034         [ +  + ]:           1208 :                     else if (tm->tm_mon < 0)
                               3035                 :                :                     {
                               3036                 :                :                         /*
                               3037                 :                :                          * Negative case.  In this case, the calculation is
                               3038                 :                :                          * reversed, where -1 means December, -2 November,
                               3039                 :                :                          * etc.
                               3040                 :                :                          */
                               3041                 :             96 :                         mon = -1 * (tm->tm_mon + 1);
                               3042                 :                :                     }
                               3043                 :                :                     else
                               3044                 :                :                     {
                               3045                 :                :                         /*
                               3046                 :                :                          * Common case, with a strictly positive value.  The
                               3047                 :                :                          * position in the array matches with the value of
                               3048                 :                :                          * tm_mon.
                               3049                 :                :                          */
                               3050                 :           1112 :                         mon = MONTHS_PER_YEAR - tm->tm_mon;
                               3051                 :                :                     }
                               3052                 :                : 
    7 tgl@sss.pgh.pa.us        3053         [ +  + ]:GNC        1224 :                     DCH_EMITF("%*s", IS_SUFFIX_FM(n->suffix) ? 0 : -4,
                               3054                 :                :                               months[mon]);
                               3055                 :                :                 }
 6732 tgl@sss.pgh.pa.us        3056                 :CBC        1224 :                 break;
 6732 tgl@sss.pgh.pa.us        3057                 :UBC           0 :             case DCH_W:
    7 tgl@sss.pgh.pa.us        3058                 :UNC           0 :                 DCH_EMITF("%d", (tm->tm_mday - 1) / 7 + 1);
  300 peter@eisentraut.org     3059         [ #  # ]:UBC           0 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        3060                 :UNC           0 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        3061                 :UBC           0 :                 break;
 6732 tgl@sss.pgh.pa.us        3062                 :CBC        1524 :             case DCH_J:
    7 tgl@sss.pgh.pa.us        3063                 :GNC        1524 :                 DCH_EMITF("%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
  300 peter@eisentraut.org     3064         [ +  + ]:CBC        1524 :                 if (IS_SUFFIX_THth(n->suffix))
    7 tgl@sss.pgh.pa.us        3065                 :GNC         508 :                     str_numth(out, field_start, SUFFIX_TH_TYPE(n->suffix));
 6732 tgl@sss.pgh.pa.us        3066                 :CBC        1524 :                 break;
                               3067                 :                :         }
                               3068                 :                :     }
                               3069                 :                : 
                               3070                 :                : #undef DCH_EMITF
                               3071                 :                : #undef DCH_EMITS
                               3072                 :                : #undef DCH_EMITC
                               3073                 :           6308 : }
                               3074                 :                : 
                               3075                 :                : /*
                               3076                 :                :  * Process the string 'in' as denoted by the array of FormatNodes 'node[]'.
                               3077                 :                :  * The TmFromChar struct pointed to by 'out' is populated with the results.
                               3078                 :                :  *
                               3079                 :                :  * 'collid' identifies the collation to use, if needed.
                               3080                 :                :  * 'std' specifies standard parsing mode.
                               3081                 :                :  *
                               3082                 :                :  * If escontext points to an ErrorSaveContext, data errors will be reported
                               3083                 :                :  * by filling that struct; the caller must test SOFT_ERROR_OCCURRED() to see
                               3084                 :                :  * whether an error occurred.  Otherwise, errors are thrown.
                               3085                 :                :  *
                               3086                 :                :  * Note: we currently don't have any to_interval() function, so there
                               3087                 :                :  * is no need here for INVALID_FOR_INTERVAL checks.
                               3088                 :                :  */
                               3089                 :                : static void
 2368                          3090                 :          26989 : DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
                               3091                 :                :               Oid collid, bool std, Node *escontext)
                               3092                 :                : {
                               3093                 :                :     FormatNode *n;
                               3094                 :                :     const char *s;
                               3095                 :                :     int         len,
                               3096                 :                :                 value;
 2528 akorotkov@postgresql     3097                 :          26989 :     bool        fx_mode = std;
                               3098                 :                : 
                               3099                 :                :     /* number of extra skipped characters (more than given in format string) */
 2909                          3100                 :          26989 :     int         extra_skip = 0;
                               3101                 :                : 
                               3102                 :                :     /* cache localized days and months */
 2368 tgl@sss.pgh.pa.us        3103                 :          26989 :     cache_locale_time();
                               3104                 :                : 
 6732                          3105   [ +  +  +  + ]:         162275 :     for (n = node, s = in; n->type != NODE_TYPE_END && *s != '\0'; n++)
                               3106                 :                :     {
                               3107                 :                :         /*
                               3108                 :                :          * Ignore spaces at the beginning of the string and before fields when
                               3109                 :                :          * not in FX (fixed width) mode.
                               3110                 :                :          */
 2909 akorotkov@postgresql     3111   [ +  +  +  +  :         149581 :         if (!fx_mode && (n->type != NODE_TYPE_ACTION || n->key->id != DCH_FX) &&
                                              +  + ]
                               3112   [ +  +  +  + ]:           6217 :             (n->type == NODE_TYPE_ACTION || n == node))
                               3113                 :                :         {
                               3114   [ +  -  +  + ]:           3509 :             while (*s != '\0' && isspace((unsigned char) *s))
                               3115                 :                :             {
                               3116                 :             56 :                 s++;
                               3117                 :             56 :                 extra_skip++;
                               3118                 :                :             }
                               3119                 :                :         }
                               3120                 :                : 
                               3121   [ +  +  +  + ]:         149581 :         if (n->type == NODE_TYPE_SPACE || n->type == NODE_TYPE_SEPARATOR)
                               3122                 :                :         {
 2528                          3123         [ +  + ]:          64878 :             if (std)
                               3124                 :                :             {
                               3125                 :                :                 /*
                               3126                 :                :                  * Standard mode requires strict matching between format
                               3127                 :                :                  * string separators/spaces and input string.
                               3128                 :                :                  */
                               3129   [ +  -  -  + ]:          62386 :                 Assert(n->character[0] && !n->character[1]);
                               3130                 :                : 
                               3131         [ +  + ]:          62386 :                 if (*s == n->character[0])
                               3132                 :          50494 :                     s++;
                               3133                 :                :                 else
 1357 tgl@sss.pgh.pa.us        3134         [ -  + ]:          20750 :                     ereturn(escontext,,
                               3135                 :                :                             (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               3136                 :                :                              errmsg("unmatched format separator \"%c\"",
                               3137                 :                :                                     n->character[0])));
                               3138                 :                :             }
 2528 akorotkov@postgresql     3139         [ +  + ]:           2492 :             else if (!fx_mode)
                               3140                 :                :             {
                               3141                 :                :                 /*
                               3142                 :                :                  * In non FX (fixed format) mode one format string space or
                               3143                 :                :                  * separator match to one space or separator in input string.
                               3144                 :                :                  * Or match nothing if there is no space or separator in the
                               3145                 :                :                  * current position of input string.
                               3146                 :                :                  */
 2909                          3147                 :           2476 :                 extra_skip--;
                               3148   [ +  +  +  + ]:           2476 :                 if (isspace((unsigned char) *s) || is_separator_char(s))
                               3149                 :                :                 {
                               3150                 :           1772 :                     s++;
                               3151                 :           1772 :                     extra_skip++;
                               3152                 :                :                 }
                               3153                 :                :             }
                               3154                 :                :             else
                               3155                 :                :             {
                               3156                 :                :                 /*
                               3157                 :                :                  * In FX mode, on format string space or separator we consume
                               3158                 :                :                  * exactly one character from input string.  Notice we don't
                               3159                 :                :                  * insist that the consumed character match the format's
                               3160                 :                :                  * character.
                               3161                 :                :                  */
  232 tmunro@postgresql.or     3162                 :             16 :                 s += pg_mblen_cstr(s);
                               3163                 :                :             }
 2909 akorotkov@postgresql     3164                 :          52986 :             continue;
                               3165                 :                :         }
                               3166         [ +  + ]:          84703 :         else if (n->type != NODE_TYPE_ACTION)
                               3167                 :                :         {
                               3168                 :                :             /*
                               3169                 :                :              * Text character, so consume one character from input string.
                               3170                 :                :              * Notice we don't insist that the consumed character match the
                               3171                 :                :              * format's character.
                               3172                 :                :              */
 2898                          3173         [ +  + ]:           2126 :             if (!fx_mode)
                               3174                 :                :             {
                               3175                 :                :                 /*
                               3176                 :                :                  * In non FX mode we might have skipped some extra characters
                               3177                 :                :                  * (more than specified in format string) before.  In this
                               3178                 :                :                  * case we don't skip input string character, because it might
                               3179                 :                :                  * be part of field.
                               3180                 :                :                  */
                               3181         [ +  + ]:            292 :                 if (extra_skip > 0)
                               3182                 :             16 :                     extra_skip--;
                               3183                 :                :                 else
  232 tmunro@postgresql.or     3184                 :            276 :                     s += pg_mblen_cstr(s);
                               3185                 :                :             }
                               3186                 :                :             else
                               3187                 :                :             {
                               3188                 :           1834 :                 int         chlen = pg_mblen_cstr(s);
                               3189                 :                : 
                               3190                 :                :                 /*
                               3191                 :                :                  * Standard mode requires strict match of format characters.
                               3192                 :                :                  */
 2158 akorotkov@postgresql     3193   [ +  -  +  - ]:           1834 :                 if (std && n->type == NODE_TYPE_CHAR &&
                               3194         [ +  + ]:           1834 :                     strncmp(s, n->character, chlen) != 0)
 1357 tgl@sss.pgh.pa.us        3195         [ +  + ]:           1810 :                     ereturn(escontext,,
                               3196                 :                :                             (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               3197                 :                :                              errmsg("unmatched format character \"%s\"",
                               3198                 :                :                                     n->character)));
                               3199                 :                : 
 2158 akorotkov@postgresql     3200                 :             24 :                 s += chlen;
                               3201                 :                :             }
 6732 tgl@sss.pgh.pa.us        3202                 :            316 :             continue;
                               3203                 :                :         }
                               3204                 :                : 
 1357                          3205         [ -  + ]:          82577 :         if (!from_char_set_mode(out, n->key->date_mode, escontext))
 1357 tgl@sss.pgh.pa.us        3206                 :UBC           0 :             return;
                               3207                 :                : 
 6732 tgl@sss.pgh.pa.us        3208   [ +  +  +  +  :CBC       82573 :         switch (n->key->id)
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                        +  +  +  +  
                                                 - ]
                               3209                 :                :         {
                               3210                 :              8 :             case DCH_FX:
                               3211                 :              8 :                 fx_mode = true;
                               3212                 :              8 :                 break;
                               3213                 :              8 :             case DCH_A_M:
                               3214                 :                :             case DCH_P_M:
                               3215                 :                :             case DCH_a_m:
                               3216                 :                :             case DCH_p_m:
 1357                          3217         [ -  + ]:              8 :                 if (!from_char_seq_search(&value, &s, ampm_strings_long,
                               3218                 :                :                                           NULL, InvalidOid,
                               3219                 :                :                                           n, escontext))
 1357 tgl@sss.pgh.pa.us        3220                 :UBC           0 :                     return;
 1357 tgl@sss.pgh.pa.us        3221         [ -  + ]:CBC           8 :                 if (!from_char_set_int(&out->pm, value % 2, n, escontext))
 1357 tgl@sss.pgh.pa.us        3222                 :UBC           0 :                     return;
  300 peter@eisentraut.org     3223                 :CBC           8 :                 out->clock_12_hour = true;
 6732 tgl@sss.pgh.pa.us        3224                 :              8 :                 break;
 6410 bruce@momjian.us         3225                 :              8 :             case DCH_AM:
                               3226                 :                :             case DCH_PM:
                               3227                 :                :             case DCH_am:
                               3228                 :                :             case DCH_pm:
 1357 tgl@sss.pgh.pa.us        3229         [ -  + ]:              8 :                 if (!from_char_seq_search(&value, &s, ampm_strings,
                               3230                 :                :                                           NULL, InvalidOid,
                               3231                 :                :                                           n, escontext))
 1357 tgl@sss.pgh.pa.us        3232                 :UBC           0 :                     return;
 1357 tgl@sss.pgh.pa.us        3233         [ -  + ]:CBC           8 :                 if (!from_char_set_int(&out->pm, value % 2, n, escontext))
 1357 tgl@sss.pgh.pa.us        3234                 :UBC           0 :                     return;
  300 peter@eisentraut.org     3235                 :CBC           8 :                 out->clock_12_hour = true;
 6732 tgl@sss.pgh.pa.us        3236                 :              8 :                 break;
                               3237                 :             96 :             case DCH_HH:
                               3238                 :                :             case DCH_HH12:
 1357                          3239         [ -  + ]:             96 :                 if (from_char_parse_int_len(&out->hh, &s, 2, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3240                 :UBC           0 :                     return;
  300 peter@eisentraut.org     3241                 :CBC          96 :                 out->clock_12_hour = true;
 3765 tgl@sss.pgh.pa.us        3242   [ -  +  -  -  :             96 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6410 bruce@momjian.us         3243                 :             96 :                 break;
 6732 tgl@sss.pgh.pa.us        3244                 :          19808 :             case DCH_HH24:
 1357                          3245         [ +  + ]:          19808 :                 if (from_char_parse_int_len(&out->hh, &s, 2, n, escontext) < 0)
                               3246                 :             96 :                     return;
 3765                          3247   [ -  +  -  -  :          19708 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3248                 :          19708 :                 break;
                               3249                 :          11492 :             case DCH_MI:
 1357                          3250         [ -  + ]:          11492 :                 if (from_char_parse_int(&out->mi, &s, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3251                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3252   [ -  +  -  -  :CBC       11492 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3253                 :          11492 :                 break;
                               3254                 :          10568 :             case DCH_SS:
 1357                          3255         [ -  + ]:          10568 :                 if (from_char_parse_int(&out->ss, &s, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3256                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3257   [ -  +  -  -  :CBC       10568 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3258                 :          10568 :                 break;
 6286 bruce@momjian.us         3259                 :              8 :             case DCH_MS:        /* millisecond */
 1357 tgl@sss.pgh.pa.us        3260                 :              8 :                 len = from_char_parse_int_len(&out->ms, &s, 3, n, escontext);
                               3261         [ -  + ]:              8 :                 if (len < 0)
 1357 tgl@sss.pgh.pa.us        3262                 :UBC           0 :                     return;
                               3263                 :                : 
                               3264                 :                :                 /*
                               3265                 :                :                  * 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25
                               3266                 :                :                  */
 6559 tgl@sss.pgh.pa.us        3267         [ +  - ]:CBC          16 :                 out->ms *= len == 1 ? 100 :
                               3268         [ -  + ]:              8 :                     len == 2 ? 10 : 1;
                               3269                 :                : 
 3765                          3270   [ -  +  -  -  :              8 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3271                 :              8 :                 break;
 2537 akorotkov@postgresql     3272                 :            172 :             case DCH_FF1:
                               3273                 :                :             case DCH_FF2:
                               3274                 :                :             case DCH_FF3:
                               3275                 :                :             case DCH_FF4:
                               3276                 :                :             case DCH_FF5:
                               3277                 :                :             case DCH_FF6:
                               3278                 :            172 :                 out->ff = n->key->id - DCH_FF1 + 1;
                               3279                 :                :                 pg_fallthrough;
 6286 bruce@momjian.us         3280                 :            888 :             case DCH_US:        /* microsecond */
 2537 akorotkov@postgresql     3281                 :            888 :                 len = from_char_parse_int_len(&out->us, &s,
                               3282         [ +  + ]:            888 :                                               n->key->id == DCH_US ? 6 :
 1357 tgl@sss.pgh.pa.us        3283                 :            172 :                                               out->ff, n, escontext);
                               3284         [ -  + ]:            888 :                 if (len < 0)
 1357 tgl@sss.pgh.pa.us        3285                 :UBC           0 :                     return;
                               3286                 :                : 
 6559 tgl@sss.pgh.pa.us        3287         [ +  + ]:CBC        1724 :                 out->us *= len == 1 ? 100000 :
                               3288         [ +  + ]:           1648 :                     len == 2 ? 10000 :
                               3289         [ +  + ]:           1016 :                     len == 3 ? 1000 :
                               3290         [ +  + ]:            304 :                     len == 4 ? 100 :
                               3291         [ +  + ]:            100 :                     len == 5 ? 10 : 1;
                               3292                 :                : 
 3765                          3293   [ -  +  -  -  :            888 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3294                 :            888 :                 break;
                               3295                 :             16 :             case DCH_SSSS:
 1357                          3296         [ -  + ]:             16 :                 if (from_char_parse_int(&out->ssss, &s, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3297                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3298   [ -  +  -  -  :CBC          16 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3299                 :             16 :                 break;
                               3300                 :           2374 :             case DCH_tz:
                               3301                 :                :             case DCH_TZ:
                               3302                 :                :                 {
                               3303                 :                :                     int         tzlen;
                               3304                 :                : 
  945                          3305                 :           2374 :                     tzlen = DecodeTimezoneAbbrevPrefix(s,
                               3306                 :                :                                                        &out->gmtoffset,
                               3307                 :                :                                                        &out->tzp);
                               3308         [ +  + ]:           2374 :                     if (tzlen > 0)
                               3309                 :                :                     {
                               3310                 :             24 :                         out->has_tz = true;
                               3311                 :                :                         /* we only need the zone abbrev for DYNTZ case */
                               3312         [ +  + ]:             24 :                         if (out->tzp)
                               3313                 :              4 :                             out->abbrev = pnstrdup(s, tzlen);
                               3314                 :             24 :                         out->tzsign = 0; /* drop any earlier TZH/TZM info */
                               3315                 :             24 :                         s += tzlen;
                               3316                 :             24 :                         break;
                               3317                 :                :                     }
                               3318         [ +  + ]:           2350 :                     else if (isalpha((unsigned char) *s))
                               3319                 :                :                     {
                               3320                 :                :                         /*
                               3321                 :                :                          * It doesn't match any abbreviation, but it starts
                               3322                 :                :                          * with a letter.  OF format certainly won't succeed;
                               3323                 :                :                          * assume it's a misspelled abbreviation and complain
                               3324                 :                :                          * accordingly.
                               3325                 :                :                          */
                               3326         [ +  - ]:              4 :                         ereturn(escontext,,
                               3327                 :                :                                 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               3328                 :                :                                  errmsg("invalid value \"%s\" for \"%s\"", s, n->key->name),
                               3329                 :                :                                  errdetail("Time zone abbreviation is not recognized.")));
                               3330                 :                :                     }
                               3331                 :                :                     /* otherwise parse it like OF */
                               3332                 :                :                 }
                               3333                 :                :                 pg_fallthrough;
                               3334                 :                :             case DCH_OF:
                               3335                 :                :                 /* OF is equivalent to TZH or TZH:TZM */
                               3336                 :                :                 /* see TZH comments below */
                               3337   [ +  +  +  +  :           2362 :                 if (*s == '+' || *s == '-' || *s == ' ')
                                              +  + ]
                               3338                 :                :                 {
                               3339         [ +  + ]:           2122 :                     out->tzsign = *s == '-' ? -1 : +1;
                               3340                 :           2122 :                     s++;
                               3341                 :                :                 }
                               3342                 :                :                 else
                               3343                 :                :                 {
                               3344   [ +  +  +  + ]:            240 :                     if (extra_skip > 0 && *(s - 1) == '-')
                               3345                 :              8 :                         out->tzsign = -1;
                               3346                 :                :                     else
                               3347                 :            232 :                         out->tzsign = +1;
                               3348                 :                :                 }
                               3349         [ +  + ]:           2362 :                 if (from_char_parse_int_len(&out->tzh, &s, 2, n, escontext) < 0)
                               3350                 :            212 :                     return;
                               3351         [ +  + ]:           2142 :                 if (*s == ':')
                               3352                 :                :                 {
                               3353                 :            222 :                     s++;
                               3354         [ -  + ]:            222 :                     if (from_char_parse_int_len(&out->tzm, &s, 2, n,
                               3355                 :                :                                                 escontext) < 0)
  945 tgl@sss.pgh.pa.us        3356                 :UBC           0 :                         return;
                               3357                 :                :                 }
 3475 tgl@sss.pgh.pa.us        3358                 :CBC        2138 :                 break;
 3152 andrew@dunslane.net      3359                 :            516 :             case DCH_TZH:
                               3360                 :                : 
                               3361                 :                :                 /*
                               3362                 :                :                  * Value of TZH might be negative.  And the issue is that we
                               3363                 :                :                  * might swallow minus sign as the separator.  So, if we have
                               3364                 :                :                  * skipped more characters than specified in the format
                               3365                 :                :                  * string, then we consider prepending last skipped minus to
                               3366                 :                :                  * TZH.
                               3367                 :                :                  */
                               3368   [ +  +  +  +  :            516 :                 if (*s == '+' || *s == '-' || *s == ' ')
                                              -  + ]
                               3369                 :                :                 {
 2909 akorotkov@postgresql     3370         [ +  + ]:            492 :                     out->tzsign = *s == '-' ? -1 : +1;
 3152 andrew@dunslane.net      3371                 :            492 :                     s++;
                               3372                 :                :                 }
                               3373                 :                :                 else
                               3374                 :                :                 {
 2909 akorotkov@postgresql     3375   [ +  +  +  + ]:             24 :                     if (extra_skip > 0 && *(s - 1) == '-')
                               3376                 :             12 :                         out->tzsign = -1;
                               3377                 :                :                     else
                               3378                 :             12 :                         out->tzsign = +1;
                               3379                 :                :                 }
                               3380                 :                : 
 1357 tgl@sss.pgh.pa.us        3381         [ -  + ]:            516 :                 if (from_char_parse_int_len(&out->tzh, &s, 2, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3382                 :UBC           0 :                     return;
 3152 andrew@dunslane.net      3383                 :CBC         516 :                 break;
                               3384                 :             52 :             case DCH_TZM:
                               3385                 :                :                 /* assign positive timezone sign if TZH was not seen before */
                               3386         [ +  + ]:             52 :                 if (!out->tzsign)
                               3387                 :              4 :                     out->tzsign = +1;
 1357 tgl@sss.pgh.pa.us        3388         [ -  + ]:             52 :                 if (from_char_parse_int_len(&out->tzm, &s, 2, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3389                 :UBC           0 :                     return;
 3152 andrew@dunslane.net      3390                 :CBC          52 :                 break;
 6732 tgl@sss.pgh.pa.us        3391                 :              8 :             case DCH_A_D:
                               3392                 :                :             case DCH_B_C:
                               3393                 :                :             case DCH_a_d:
                               3394                 :                :             case DCH_b_c:
 1357                          3395         [ -  + ]:              8 :                 if (!from_char_seq_search(&value, &s, adbc_strings_long,
                               3396                 :                :                                           NULL, InvalidOid,
                               3397                 :                :                                           n, escontext))
 1357 tgl@sss.pgh.pa.us        3398                 :UBC           0 :                     return;
 1357 tgl@sss.pgh.pa.us        3399         [ -  + ]:CBC           8 :                 if (!from_char_set_int(&out->bc, value % 2, n, escontext))
 1357 tgl@sss.pgh.pa.us        3400                 :UBC           0 :                     return;
 6732 tgl@sss.pgh.pa.us        3401                 :CBC           8 :                 break;
 6410 bruce@momjian.us         3402                 :             24 :             case DCH_AD:
                               3403                 :                :             case DCH_BC:
                               3404                 :                :             case DCH_ad:
                               3405                 :                :             case DCH_bc:
 1357 tgl@sss.pgh.pa.us        3406         [ -  + ]:             24 :                 if (!from_char_seq_search(&value, &s, adbc_strings,
                               3407                 :                :                                           NULL, InvalidOid,
                               3408                 :                :                                           n, escontext))
 1357 tgl@sss.pgh.pa.us        3409                 :UBC           0 :                     return;
 1357 tgl@sss.pgh.pa.us        3410         [ -  + ]:CBC          24 :                 if (!from_char_set_int(&out->bc, value % 2, n, escontext))
 1357 tgl@sss.pgh.pa.us        3411                 :UBC           0 :                     return;
 6732 tgl@sss.pgh.pa.us        3412                 :CBC          24 :                 break;
                               3413                 :             12 :             case DCH_MONTH:
                               3414                 :                :             case DCH_Month:
                               3415                 :                :             case DCH_month:
 1357                          3416   [ -  +  -  + ]:             12 :                 if (!from_char_seq_search(&value, &s, months_full,
  300 peter@eisentraut.org     3417                 :             12 :                                           IS_SUFFIX_TM(n->suffix) ? localized_full_months : NULL,
                               3418                 :                :                                           collid,
                               3419                 :                :                                           n, escontext))
 1357 tgl@sss.pgh.pa.us        3420                 :UBC           0 :                     return;
 1357 tgl@sss.pgh.pa.us        3421         [ -  + ]:CBC          12 :                 if (!from_char_set_int(&out->mm, value + 1, n, escontext))
 1357 tgl@sss.pgh.pa.us        3422                 :UBC           0 :                     return;
 6732 tgl@sss.pgh.pa.us        3423                 :CBC          12 :                 break;
                               3424                 :             76 :             case DCH_MON:
                               3425                 :                :             case DCH_Mon:
                               3426                 :                :             case DCH_mon:
 1357                          3427   [ -  +  -  + ]:             76 :                 if (!from_char_seq_search(&value, &s, months,
  300 peter@eisentraut.org     3428                 :             76 :                                           IS_SUFFIX_TM(n->suffix) ? localized_abbrev_months : NULL,
                               3429                 :                :                                           collid,
                               3430                 :                :                                           n, escontext))
 1357 tgl@sss.pgh.pa.us        3431                 :UBC           0 :                     return;
 1357 tgl@sss.pgh.pa.us        3432         [ -  + ]:CBC          68 :                 if (!from_char_set_int(&out->mm, value + 1, n, escontext))
 1357 tgl@sss.pgh.pa.us        3433                 :UBC           0 :                     return;
 6732 tgl@sss.pgh.pa.us        3434                 :CBC          64 :                 break;
                               3435                 :          11448 :             case DCH_MM:
 1357                          3436         [ -  + ]:          11448 :                 if (from_char_parse_int(&out->mm, &s, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3437                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3438   [ -  +  -  -  :CBC       11436 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3439                 :          11436 :                 break;
                               3440                 :              4 :             case DCH_DAY:
                               3441                 :                :             case DCH_Day:
                               3442                 :                :             case DCH_day:
 1357                          3443   [ -  +  -  + ]:              4 :                 if (!from_char_seq_search(&value, &s, days,
  300 peter@eisentraut.org     3444                 :              4 :                                           IS_SUFFIX_TM(n->suffix) ? localized_full_days : NULL,
                               3445                 :                :                                           collid,
                               3446                 :                :                                           n, escontext))
 1357 tgl@sss.pgh.pa.us        3447                 :UBC           0 :                     return;
 1357 tgl@sss.pgh.pa.us        3448         [ -  + ]:CBC           4 :                 if (!from_char_set_int(&out->d, value, n, escontext))
 1357 tgl@sss.pgh.pa.us        3449                 :UBC           0 :                     return;
 5106 bruce@momjian.us         3450                 :CBC           4 :                 out->d++;
 6732 tgl@sss.pgh.pa.us        3451                 :              4 :                 break;
                               3452                 :             12 :             case DCH_DY:
                               3453                 :                :             case DCH_Dy:
                               3454                 :                :             case DCH_dy:
 1357                          3455   [ -  +  -  + ]:             12 :                 if (!from_char_seq_search(&value, &s, days_short,
  300 peter@eisentraut.org     3456                 :             12 :                                           IS_SUFFIX_TM(n->suffix) ? localized_abbrev_days : NULL,
                               3457                 :                :                                           collid,
                               3458                 :                :                                           n, escontext))
 1357 tgl@sss.pgh.pa.us        3459                 :UBC           0 :                     return;
 1357 tgl@sss.pgh.pa.us        3460         [ -  + ]:CBC          12 :                 if (!from_char_set_int(&out->d, value, n, escontext))
 1357 tgl@sss.pgh.pa.us        3461                 :UBC           0 :                     return;
 5106 bruce@momjian.us         3462                 :CBC          12 :                 out->d++;
 6732 tgl@sss.pgh.pa.us        3463                 :             12 :                 break;
                               3464                 :             24 :             case DCH_DDD:
 1357                          3465         [ -  + ]:             24 :                 if (from_char_parse_int(&out->ddd, &s, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3466                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3467   [ -  +  -  -  :CBC          24 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6559                          3468                 :             24 :                 break;
 6732                          3469                 :              4 :             case DCH_IDDD:
 1357                          3470         [ -  + ]:              4 :                 if (from_char_parse_int_len(&out->ddd, &s, 3, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3471                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3472   [ -  +  -  -  :CBC           4 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3473                 :              4 :                 break;
                               3474                 :          11489 :             case DCH_DD:
 1357                          3475         [ -  + ]:          11489 :                 if (from_char_parse_int(&out->dd, &s, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3476                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3477   [ -  +  -  -  :CBC       11480 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3478                 :          11480 :                 break;
                               3479                 :              8 :             case DCH_D:
 1357                          3480         [ -  + ]:              8 :                 if (from_char_parse_int(&out->d, &s, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3481                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3482   [ -  +  -  -  :CBC           8 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6559                          3483                 :              8 :                 break;
 6732                          3484                 :             16 :             case DCH_ID:
 1357                          3485         [ -  + ]:             16 :                 if (from_char_parse_int_len(&out->d, &s, 1, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3486                 :UBC           0 :                     return;
                               3487                 :                :                 /* Shift numbering to match Gregorian where Sunday = 1 */
 5106 bruce@momjian.us         3488         [ +  - ]:CBC          16 :                 if (++out->d > 7)
                               3489                 :             16 :                     out->d = 1;
 3765 tgl@sss.pgh.pa.us        3490   [ -  +  -  -  :             16 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3491                 :             16 :                 break;
                               3492                 :             24 :             case DCH_WW:
                               3493                 :                :             case DCH_IW:
 1357                          3494         [ -  + ]:             24 :                 if (from_char_parse_int(&out->ww, &s, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3495                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3496   [ -  +  -  -  :CBC          24 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3497                 :             24 :                 break;
                               3498                 :              4 :             case DCH_Q:
                               3499                 :                : 
                               3500                 :                :                 /*
                               3501                 :                :                  * We ignore 'Q' when converting to date because it is unclear
                               3502                 :                :                  * which date in the quarter to use, and some people specify
                               3503                 :                :                  * both quarter and month, so if it was honored it might
                               3504                 :                :                  * conflict with the supplied month. That is also why we don't
                               3505                 :                :                  * throw an error.
                               3506                 :                :                  *
                               3507                 :                :                  * We still parse the source string for an integer, but it
                               3508                 :                :                  * isn't stored anywhere in 'out'.
                               3509                 :                :                  */
 1357                          3510         [ -  + ]:              4 :                 if (from_char_parse_int((int *) NULL, &s, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3511                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3512   [ -  +  -  -  :CBC           4 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3513                 :              4 :                 break;
                               3514                 :             20 :             case DCH_CC:
 1357                          3515         [ -  + ]:             20 :                 if (from_char_parse_int(&out->cc, &s, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3516                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3517   [ -  +  -  -  :CBC          20 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3518                 :             20 :                 break;
                               3519                 :              8 :             case DCH_Y_YYY:
                               3520                 :                :                 {
                               3521                 :                :                     int         matched,
                               3522                 :                :                                 years,
                               3523                 :                :                                 millennia,
                               3524                 :                :                                 nch;
                               3525                 :                : 
 3737 stark@mit.edu            3526                 :              8 :                     matched = sscanf(s, "%d,%03d%n", &millennia, &years, &nch);
 3765 tgl@sss.pgh.pa.us        3527         [ -  + ]:              8 :                     if (matched < 2)
 1357 tgl@sss.pgh.pa.us        3528         [ #  # ]:UBC           0 :                         ereturn(escontext,,
                               3529                 :                :                                 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               3530                 :                :                                  errmsg("invalid value \"%s\" for \"%s\"", s, "Y,YYY")));
                               3531                 :                : 
                               3532                 :                :                     /* years += (millennia * 1000); */
  626 nathan@postgresql.or     3533   [ +  +  -  + ]:CBC          12 :                     if (pg_mul_s32_overflow(millennia, 1000, &millennia) ||
                               3534                 :              4 :                         pg_add_s32_overflow(years, millennia, &years))
                               3535         [ +  - ]:              4 :                         ereturn(escontext,,
                               3536                 :                :                                 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
                               3537                 :                :                                  errmsg("value for \"%s\" in source string is out of range", "Y,YYY")));
                               3538                 :                : 
 1357 tgl@sss.pgh.pa.us        3539         [ -  + ]:              4 :                     if (!from_char_set_int(&out->year, years, n, escontext))
 1357 tgl@sss.pgh.pa.us        3540                 :UBC           0 :                         return;
 6732 tgl@sss.pgh.pa.us        3541                 :CBC           4 :                     out->yysz = 4;
 3765                          3542                 :              4 :                     s += nch;
                               3543   [ +  -  +  -  :              4 :                     SKIP_THth(s, n->suffix);
                                              +  - ]
                               3544                 :                :                 }
 6732                          3545                 :              4 :                 break;
 6559                          3546                 :          13462 :             case DCH_YYYY:
                               3547                 :                :             case DCH_IYYY:
 1357                          3548         [ +  + ]:          13462 :                 if (from_char_parse_int(&out->year, &s, n, escontext) < 0)
                               3549                 :            216 :                     return;
 6559                          3550                 :          13238 :                 out->yysz = 4;
 3765                          3551   [ -  +  -  -  :          13238 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6559                          3552                 :          13238 :                 break;
 6732                          3553                 :              8 :             case DCH_YYY:
                               3554                 :                :             case DCH_IYY:
 1357                          3555                 :              8 :                 len = from_char_parse_int(&out->year, &s, n, escontext);
                               3556         [ -  + ]:              8 :                 if (len < 0)
 1357 tgl@sss.pgh.pa.us        3557                 :UBC           0 :                     return;
 2528 akorotkov@postgresql     3558         [ +  - ]:CBC           8 :                 if (len < 4)
 5468 bruce@momjian.us         3559                 :              8 :                     out->year = adjust_partial_year_to_2020(out->year);
 6559 tgl@sss.pgh.pa.us        3560                 :              8 :                 out->yysz = 3;
 3765                          3561   [ -  +  -  -  :              8 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3562                 :              8 :                 break;
                               3563                 :             40 :             case DCH_YY:
                               3564                 :                :             case DCH_IY:
 1357                          3565                 :             40 :                 len = from_char_parse_int(&out->year, &s, n, escontext);
                               3566         [ -  + ]:             40 :                 if (len < 0)
 1357 tgl@sss.pgh.pa.us        3567                 :UBC           0 :                     return;
 2528 akorotkov@postgresql     3568         [ +  - ]:CBC          40 :                 if (len < 4)
 5468 bruce@momjian.us         3569                 :             40 :                     out->year = adjust_partial_year_to_2020(out->year);
 6559 tgl@sss.pgh.pa.us        3570                 :             40 :                 out->yysz = 2;
 3765                          3571   [ -  +  -  -  :             40 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3572                 :             40 :                 break;
                               3573                 :              8 :             case DCH_Y:
                               3574                 :                :             case DCH_I:
 1357                          3575                 :              8 :                 len = from_char_parse_int(&out->year, &s, n, escontext);
                               3576         [ -  + ]:              8 :                 if (len < 0)
 1357 tgl@sss.pgh.pa.us        3577                 :UBC           0 :                     return;
 2528 akorotkov@postgresql     3578         [ +  - ]:CBC           8 :                 if (len < 4)
 5468 bruce@momjian.us         3579                 :              8 :                     out->year = adjust_partial_year_to_2020(out->year);
 6559 tgl@sss.pgh.pa.us        3580                 :              8 :                 out->yysz = 1;
 3765                          3581   [ -  +  -  -  :              8 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3582                 :              8 :                 break;
                               3583                 :              4 :             case DCH_RM:
                               3584                 :                :             case DCH_rm:
 1357                          3585         [ -  + ]:              4 :                 if (!from_char_seq_search(&value, &s, rm_months_lower,
                               3586                 :                :                                           NULL, InvalidOid,
                               3587                 :                :                                           n, escontext))
 1357 tgl@sss.pgh.pa.us        3588                 :UBC           0 :                     return;
 1357 tgl@sss.pgh.pa.us        3589         [ -  + ]:CBC           4 :                 if (!from_char_set_int(&out->mm, MONTHS_PER_YEAR - value, n,
                               3590                 :                :                                        escontext))
 1357 tgl@sss.pgh.pa.us        3591                 :UBC           0 :                     return;
 6732 tgl@sss.pgh.pa.us        3592                 :CBC           4 :                 break;
                               3593                 :              8 :             case DCH_W:
 1357                          3594         [ -  + ]:              8 :                 if (from_char_parse_int(&out->w, &s, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3595                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3596   [ -  +  -  -  :CBC           8 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3597                 :              8 :                 break;
                               3598                 :              4 :             case DCH_J:
 1357                          3599         [ -  + ]:              4 :                 if (from_char_parse_int(&out->j, &s, n, escontext) < 0)
 1357 tgl@sss.pgh.pa.us        3600                 :UBC           0 :                     return;
 3765 tgl@sss.pgh.pa.us        3601   [ -  +  -  -  :CBC           4 :                 SKIP_THth(s, n->suffix);
                                              -  - ]
 6732                          3602                 :              4 :                 break;
                               3603                 :                :         }
                               3604                 :                : 
                               3605                 :                :         /* Ignore all spaces after fields */
 2909 akorotkov@postgresql     3606         [ +  + ]:          81984 :         if (!fx_mode)
                               3607                 :                :         {
                               3608                 :           3384 :             extra_skip = 0;
                               3609   [ +  +  +  + ]:           4180 :             while (*s != '\0' && isspace((unsigned char) *s))
                               3610                 :                :             {
                               3611                 :            796 :                 s++;
                               3612                 :            796 :                 extra_skip++;
                               3613                 :                :             }
                               3614                 :                :         }
                               3615                 :                :     }
                               3616                 :                : 
                               3617                 :                :     /*
                               3618                 :                :      * Standard parsing mode doesn't allow unmatched format patterns or
                               3619                 :                :      * trailing characters in the input string.
                               3620                 :                :      */
 2528                          3621         [ +  + ]:          12694 :     if (std)
                               3622                 :                :     {
                               3623         [ +  + ]:          12010 :         if (n->type != NODE_TYPE_END)
 1357 tgl@sss.pgh.pa.us        3624         [ +  + ]:           4470 :             ereturn(escontext,,
                               3625                 :                :                     (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               3626                 :                :                      errmsg("input string is too short for datetime format")));
                               3627                 :                : 
 2528 akorotkov@postgresql     3628   [ +  +  +  + ]:           9602 :         while (*s != '\0' && isspace((unsigned char) *s))
                               3629                 :           2062 :             s++;
                               3630                 :                : 
                               3631         [ +  + ]:           7540 :         if (*s != '\0')
 1357 tgl@sss.pgh.pa.us        3632         [ +  + ]:           2086 :             ereturn(escontext,,
                               3633                 :                :                     (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               3634                 :                :                      errmsg("trailing characters remain in input string after datetime format")));
                               3635                 :                :     }
                               3636                 :                : }
                               3637                 :                : 
                               3638                 :                : /*
                               3639                 :                :  * The invariant for DCH cache entry management is that DCHCounter is equal
                               3640                 :                :  * to the maximum age value among the existing entries, and we increment it
                               3641                 :                :  * whenever an access occurs.  If we approach overflow, deal with that by
                               3642                 :                :  * halving all the age values, so that we retain a fairly accurate idea of
                               3643                 :                :  * which entries are oldest.
                               3644                 :                :  */
                               3645                 :                : static inline void
 2872                          3646                 :          33968 : DCH_prevent_counter_overflow(void)
                               3647                 :                : {
                               3648         [ -  + ]:          33968 :     if (DCHCounter >= (INT_MAX - 1))
                               3649                 :                :     {
 2872 tgl@sss.pgh.pa.us        3650         [ #  # ]:UBC           0 :         for (int i = 0; i < n_DCHCache; i++)
                               3651                 :              0 :             DCHCache[i]->age >>= 1;
                               3652                 :              0 :         DCHCounter >>= 1;
                               3653                 :                :     }
 2872 tgl@sss.pgh.pa.us        3654                 :CBC       33968 : }
                               3655                 :                : 
                               3656                 :                : /*
                               3657                 :                :  * Get mask of date/time/zone components present in format nodes.
                               3658                 :                :  */
                               3659                 :                : static int
 1357                          3660                 :           5498 : DCH_datetime_type(FormatNode *node)
                               3661                 :                : {
 2528 akorotkov@postgresql     3662                 :           5498 :     int         flags = 0;
                               3663                 :                : 
  303 peter@eisentraut.org     3664         [ +  + ]:          50574 :     for (FormatNode *n = node; n->type != NODE_TYPE_END; n++)
                               3665                 :                :     {
 2528 akorotkov@postgresql     3666         [ +  + ]:          45076 :         if (n->type != NODE_TYPE_ACTION)
                               3667                 :          18726 :             continue;
                               3668                 :                : 
                               3669   [ -  +  +  +  :          26350 :         switch (n->key->id)
                                                 - ]
                               3670                 :                :         {
 2528 akorotkov@postgresql     3671                 :UBC           0 :             case DCH_FX:
                               3672                 :              0 :                 break;
 2528 akorotkov@postgresql     3673                 :CBC       13540 :             case DCH_A_M:
                               3674                 :                :             case DCH_P_M:
                               3675                 :                :             case DCH_a_m:
                               3676                 :                :             case DCH_p_m:
                               3677                 :                :             case DCH_AM:
                               3678                 :                :             case DCH_PM:
                               3679                 :                :             case DCH_am:
                               3680                 :                :             case DCH_pm:
                               3681                 :                :             case DCH_HH:
                               3682                 :                :             case DCH_HH12:
                               3683                 :                :             case DCH_HH24:
                               3684                 :                :             case DCH_MI:
                               3685                 :                :             case DCH_SS:
                               3686                 :                :             case DCH_MS:        /* millisecond */
                               3687                 :                :             case DCH_US:        /* microsecond */
                               3688                 :                :             case DCH_FF1:
                               3689                 :                :             case DCH_FF2:
                               3690                 :                :             case DCH_FF3:
                               3691                 :                :             case DCH_FF4:
                               3692                 :                :             case DCH_FF5:
                               3693                 :                :             case DCH_FF6:
                               3694                 :                :             case DCH_SSSS:
                               3695                 :          13540 :                 flags |= DCH_TIMED;
                               3696                 :          13540 :                 break;
                               3697                 :           2682 :             case DCH_tz:
                               3698                 :                :             case DCH_TZ:
                               3699                 :                :             case DCH_OF:
                               3700                 :                :             case DCH_TZH:
                               3701                 :                :             case DCH_TZM:
                               3702                 :           2682 :                 flags |= DCH_ZONED;
                               3703                 :           2682 :                 break;
                               3704                 :          10128 :             case DCH_A_D:
                               3705                 :                :             case DCH_B_C:
                               3706                 :                :             case DCH_a_d:
                               3707                 :                :             case DCH_b_c:
                               3708                 :                :             case DCH_AD:
                               3709                 :                :             case DCH_BC:
                               3710                 :                :             case DCH_ad:
                               3711                 :                :             case DCH_bc:
                               3712                 :                :             case DCH_MONTH:
                               3713                 :                :             case DCH_Month:
                               3714                 :                :             case DCH_month:
                               3715                 :                :             case DCH_MON:
                               3716                 :                :             case DCH_Mon:
                               3717                 :                :             case DCH_mon:
                               3718                 :                :             case DCH_MM:
                               3719                 :                :             case DCH_DAY:
                               3720                 :                :             case DCH_Day:
                               3721                 :                :             case DCH_day:
                               3722                 :                :             case DCH_DY:
                               3723                 :                :             case DCH_Dy:
                               3724                 :                :             case DCH_dy:
                               3725                 :                :             case DCH_DDD:
                               3726                 :                :             case DCH_IDDD:
                               3727                 :                :             case DCH_DD:
                               3728                 :                :             case DCH_D:
                               3729                 :                :             case DCH_ID:
                               3730                 :                :             case DCH_WW:
                               3731                 :                :             case DCH_Q:
                               3732                 :                :             case DCH_CC:
                               3733                 :                :             case DCH_Y_YYY:
                               3734                 :                :             case DCH_YYYY:
                               3735                 :                :             case DCH_IYYY:
                               3736                 :                :             case DCH_YYY:
                               3737                 :                :             case DCH_IYY:
                               3738                 :                :             case DCH_YY:
                               3739                 :                :             case DCH_IY:
                               3740                 :                :             case DCH_Y:
                               3741                 :                :             case DCH_I:
                               3742                 :                :             case DCH_RM:
                               3743                 :                :             case DCH_rm:
                               3744                 :                :             case DCH_W:
                               3745                 :                :             case DCH_J:
                               3746                 :          10128 :                 flags |= DCH_DATED;
                               3747                 :          10128 :                 break;
                               3748                 :                :         }
                               3749                 :                :     }
                               3750                 :                : 
                               3751                 :           5498 :     return flags;
                               3752                 :                : }
                               3753                 :                : 
                               3754                 :                : /* select a DCHCacheEntry to hold the given format picture */
                               3755                 :                : static DCHCacheEntry *
                               3756                 :            623 : DCH_cache_getnew(const char *str, bool std)
                               3757                 :                : {
                               3758                 :                :     DCHCacheEntry *ent;
                               3759                 :                : 
                               3760                 :                :     /* Ensure we can advance DCHCounter below */
 2872 tgl@sss.pgh.pa.us        3761                 :            623 :     DCH_prevent_counter_overflow();
                               3762                 :                : 
                               3763                 :                :     /*
                               3764                 :                :      * If cache is full, remove oldest entry (or recycle first not-valid one)
                               3765                 :                :      */
 3620                          3766         [ +  + ]:            623 :     if (n_DCHCache >= DCH_CACHE_ENTRIES)
                               3767                 :                :     {
 2872                          3768                 :            308 :         DCHCacheEntry *old = DCHCache[0];
                               3769                 :                : 
                               3770                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               3771                 :                :         elog(DEBUG_elog_output, "cache is full (%d)", n_DCHCache);
                               3772                 :                : #endif
 3620                          3773         [ +  - ]:            308 :         if (old->valid)
                               3774                 :                :         {
 2872                          3775         [ +  + ]:           6132 :             for (int i = 1; i < DCH_CACHE_ENTRIES; i++)
                               3776                 :                :             {
                               3777                 :           5828 :                 ent = DCHCache[i];
 3620                          3778         [ +  + ]:           5828 :                 if (!ent->valid)
                               3779                 :                :                 {
                               3780                 :              4 :                     old = ent;
                               3781                 :              4 :                     break;
                               3782                 :                :                 }
                               3783         [ +  + ]:           5824 :                 if (ent->age < old->age)
                               3784                 :            512 :                     old = ent;
                               3785                 :                :             }
                               3786                 :                :         }
                               3787                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               3788                 :                :         elog(DEBUG_elog_output, "OLD: '%s' AGE: %d", old->str, old->age);
                               3789                 :                : #endif
                               3790                 :            308 :         old->valid = false;
 2208 peter@eisentraut.org     3791                 :            308 :         strlcpy(old->str, str, DCH_CACHE_SIZE + 1);
 9660 bruce@momjian.us         3792                 :            308 :         old->age = (++DCHCounter);
                               3793                 :                :         /* caller is expected to fill format, then set valid */
                               3794                 :            308 :         return old;
                               3795                 :                :     }
                               3796                 :                :     else
                               3797                 :                :     {
                               3798                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               3799                 :                :         elog(DEBUG_elog_output, "NEW (%d)", n_DCHCache);
                               3800                 :                : #endif
 2872 tgl@sss.pgh.pa.us        3801         [ -  + ]:            315 :         Assert(DCHCache[n_DCHCache] == NULL);
                               3802                 :            315 :         DCHCache[n_DCHCache] = ent = (DCHCacheEntry *)
                               3803                 :            315 :             MemoryContextAllocZero(TopMemoryContext, sizeof(DCHCacheEntry));
 3620                          3804                 :            315 :         ent->valid = false;
 2208 peter@eisentraut.org     3805                 :            315 :         strlcpy(ent->str, str, DCH_CACHE_SIZE + 1);
 2528 akorotkov@postgresql     3806                 :            315 :         ent->std = std;
 9660 bruce@momjian.us         3807                 :            315 :         ent->age = (++DCHCounter);
                               3808                 :                :         /* caller is expected to fill format, then set valid */
                               3809                 :            315 :         ++n_DCHCache;
                               3810                 :            315 :         return ent;
                               3811                 :                :     }
                               3812                 :                : }
                               3813                 :                : 
                               3814                 :                : /* look for an existing DCHCacheEntry matching the given format picture */
                               3815                 :                : static DCHCacheEntry *
 2528 akorotkov@postgresql     3816                 :          33345 : DCH_cache_search(const char *str, bool std)
                               3817                 :                : {
                               3818                 :                :     /* Ensure we can advance DCHCounter below */
 2872 tgl@sss.pgh.pa.us        3819                 :          33345 :     DCH_prevent_counter_overflow();
                               3820                 :                : 
                               3821         [ +  + ]:         176868 :     for (int i = 0; i < n_DCHCache; i++)
                               3822                 :                :     {
                               3823                 :         176245 :         DCHCacheEntry *ent = DCHCache[i];
                               3824                 :                : 
 2528 akorotkov@postgresql     3825   [ +  +  +  +  :         176245 :         if (ent->valid && strcmp(ent->str, str) == 0 && ent->std == std)
                                              +  - ]
                               3826                 :                :         {
 9660 bruce@momjian.us         3827                 :          32722 :             ent->age = (++DCHCounter);
                               3828                 :          32722 :             return ent;
                               3829                 :                :         }
                               3830                 :                :     }
                               3831                 :                : 
 8268 neilc@samurai.com        3832                 :            623 :     return NULL;
                               3833                 :                : }
                               3834                 :                : 
                               3835                 :                : /* Find or create a DCHCacheEntry for the given format picture */
                               3836                 :                : static DCHCacheEntry *
 2528 akorotkov@postgresql     3837                 :          33345 : DCH_cache_fetch(const char *str, bool std)
                               3838                 :                : {
                               3839                 :                :     DCHCacheEntry *ent;
                               3840                 :                : 
                               3841         [ +  + ]:          33345 :     if ((ent = DCH_cache_search(str, std)) == NULL)
                               3842                 :                :     {
                               3843                 :                :         /*
                               3844                 :                :          * Not in the cache, must run parser and save a new format-picture to
                               3845                 :                :          * the cache.  Do not mark the cache entry valid until parsing
                               3846                 :                :          * succeeds.
                               3847                 :                :          */
                               3848                 :            623 :         ent = DCH_cache_getnew(str, std);
                               3849                 :                : 
                               3850         [ +  + ]:            623 :         parse_format(ent->format, str, DCH_keywords, DCH_suff, DCH_index,
                               3851                 :                :                      DCH_FLAG | (std ? STD_FLAG : 0), NULL);
                               3852                 :                : 
 3620 tgl@sss.pgh.pa.us        3853                 :            619 :         ent->valid = true;
                               3854                 :                :     }
                               3855                 :          33341 :     return ent;
                               3856                 :                : }
                               3857                 :                : 
                               3858                 :                : /*
                               3859                 :                :  * Format a date/time or interval into a string according to fmt.
                               3860                 :                :  * We parse fmt into a list of FormatNodes.  This is then passed to DCH_to_char
                               3861                 :                :  * for formatting.
                               3862                 :                :  */
                               3863                 :                : static text *
    7 tgl@sss.pgh.pa.us        3864                 :GNC        6308 : datetime_to_char_body(const TmToChar *tmtc, const text *fmt,
                               3865                 :                :                       bool is_interval, Oid collid)
                               3866                 :                : {
                               3867                 :                :     FormatNode *format;
                               3868                 :                :     char       *fmt_str;
                               3869                 :                :     size_t      fmt_len;
                               3870                 :                :     bool        incache;
                               3871                 :                :     StringInfoData result;
                               3872                 :                : 
                               3873                 :                :     /*
                               3874                 :                :      * Convert fmt to C string
                               3875                 :                :      */
 6729 tgl@sss.pgh.pa.us        3876                 :CBC        6308 :     fmt_str = text_to_cstring(fmt);
                               3877                 :           6308 :     fmt_len = strlen(fmt_str);
                               3878                 :                : 
                               3879                 :                :     /*
                               3880                 :                :      * Create workspace to hold result.  We'll use result.data directly as the
                               3881                 :                :      * returned TEXT datum, so leave enough room for the varlena header.
                               3882                 :                :      * Temporarily fill that area with spaces; that's not really necessary but
                               3883                 :                :      * it eases debugging by ensuring the result string is always printable.
                               3884                 :                :      */
    7 tgl@sss.pgh.pa.us        3885                 :GNC        6308 :     initStringInfo(&result);
                               3886                 :           6308 :     enlargeStringInfo(&result, VARHDRSZ);   /* just pro-forma */
                               3887                 :           6308 :     memset(result.data, ' ', VARHDRSZ);
                               3888                 :           6308 :     result.len = VARHDRSZ;
                               3889                 :           6308 :     result.data[VARHDRSZ] = '\0';   /* maintain StringInfo's invariant */
                               3890                 :                : 
 8394 tgl@sss.pgh.pa.us        3891         [ -  + ]:CBC        6308 :     if (fmt_len > DCH_CACHE_SIZE)
                               3892                 :                :     {
                               3893                 :                :         /*
                               3894                 :                :          * Allocate new memory if format picture is bigger than static cache
                               3895                 :                :          * and do not use cache (call parser always)
                               3896                 :                :          */
 3298 peter_e@gmx.net          3897                 :UBC           0 :         incache = false;
                               3898                 :                : 
  108 nathan@postgresql.or     3899                 :              0 :         format = palloc_array(FormatNode, fmt_len + 1);
                               3900                 :                : 
 8394 tgl@sss.pgh.pa.us        3901                 :              0 :         parse_format(format, fmt_str, DCH_keywords,
                               3902                 :                :                      DCH_suff, DCH_index, DCH_FLAG, NULL);
                               3903                 :                :     }
                               3904                 :                :     else
                               3905                 :                :     {
                               3906                 :                :         /*
                               3907                 :                :          * Use cache buffers
                               3908                 :                :          */
 2528 akorotkov@postgresql     3909                 :CBC        6308 :         DCHCacheEntry *ent = DCH_cache_fetch(fmt_str, false);
                               3910                 :                : 
 3298 peter_e@gmx.net          3911                 :           6308 :         incache = true;
 9633 bruce@momjian.us         3912                 :           6308 :         format = ent->format;
                               3913                 :                :     }
                               3914                 :                : 
                               3915                 :                :     /* The real work is here */
    7 tgl@sss.pgh.pa.us        3916                 :GNC        6308 :     DCH_to_char(format, is_interval, collid, tmtc, &result);
                               3917                 :                : 
 9121 bruce@momjian.us         3918         [ -  + ]:CBC        6308 :     if (!incache)
 9711 bruce@momjian.us         3919                 :UBC           0 :         pfree(format);
                               3920                 :                : 
 8394 tgl@sss.pgh.pa.us        3921                 :CBC        6308 :     pfree(fmt_str);
                               3922                 :                : 
                               3923                 :                :     /* Insert the varlena header needed to make result a valid TEXT datum */
    7 tgl@sss.pgh.pa.us        3924                 :GNC        6308 :     SET_VARSIZE(result.data, result.len);
                               3925                 :                : 
                               3926                 :           6308 :     return (text *) result.data;
                               3927                 :                : }
                               3928                 :                : 
                               3929                 :                : /****************************************************************************
                               3930                 :                :  *              Public routines
                               3931                 :                :  ***************************************************************************/
                               3932                 :                : 
                               3933                 :                : /*
                               3934                 :                :  * TIMESTAMP to_char()
                               3935                 :                :  */
                               3936                 :                : Datum
 9121 bruce@momjian.us         3937                 :CBC        3121 : timestamp_to_char(PG_FUNCTION_ARGS)
                               3938                 :                : {
 9072                          3939                 :           3121 :     Timestamp   dt = PG_GETARG_TIMESTAMP(0);
 3455 noah@leadboat.com        3940                 :           3121 :     text       *fmt = PG_GETARG_TEXT_PP(1),
                               3941                 :                :                *res;
                               3942                 :                :     TmToChar    tmtc;
                               3943                 :                :     struct pg_tm tt;
                               3944                 :                :     struct fmt_tm *tm;
                               3945                 :                :     int         thisdate;
                               3946                 :                : 
                               3947   [ +  -  +  +  :           3121 :     if (VARSIZE_ANY_EXHDR(fmt) <= 0 || TIMESTAMP_NOT_FINITE(dt))
                                              +  + ]
 9099 lockhart@fourpalms.o     3948                 :             88 :         PG_RETURN_NULL();
                               3949                 :                : 
                               3950                 :           3033 :     ZERO_tmtc(&tmtc);
 7616 tgl@sss.pgh.pa.us        3951                 :           3033 :     tm = tmtcTm(&tmtc);
                               3952                 :                : 
 1608                          3953         [ -  + ]:           3033 :     if (timestamp2tm(dt, NULL, &tt, &tmtcFsec(&tmtc), NULL, NULL) != 0)
 8432 tgl@sss.pgh.pa.us        3954         [ #  # ]:UBC           0 :         ereport(ERROR,
                               3955                 :                :                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                               3956                 :                :                  errmsg("timestamp out of range")));
                               3957                 :                : 
                               3958                 :                :     /* calculate wday and yday, because timestamp2tm doesn't */
 1607 tgl@sss.pgh.pa.us        3959                 :CBC        3033 :     thisdate = date2j(tt.tm_year, tt.tm_mon, tt.tm_mday);
                               3960                 :           3033 :     tt.tm_wday = (thisdate + 1) % 7;
                               3961                 :           3033 :     tt.tm_yday = thisdate - date2j(tt.tm_year, 1, 1) + 1;
                               3962                 :                : 
                               3963                 :           3033 :     COPY_tm(tm, &tt);
                               3964                 :                : 
 5679 peter_e@gmx.net          3965         [ -  + ]:           3033 :     if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION())))
 9099 lockhart@fourpalms.o     3966                 :UBC           0 :         PG_RETURN_NULL();
                               3967                 :                : 
 9099 lockhart@fourpalms.o     3968                 :CBC        3033 :     PG_RETURN_TEXT_P(res);
                               3969                 :                : }
                               3970                 :                : 
                               3971                 :                : Datum
                               3972                 :           3146 : timestamptz_to_char(PG_FUNCTION_ARGS)
                               3973                 :                : {
                               3974                 :           3146 :     TimestampTz dt = PG_GETARG_TIMESTAMP(0);
 3455 noah@leadboat.com        3975                 :           3146 :     text       *fmt = PG_GETARG_TEXT_PP(1),
                               3976                 :                :                *res;
                               3977                 :                :     TmToChar    tmtc;
                               3978                 :                :     int         tz;
                               3979                 :                :     struct pg_tm tt;
                               3980                 :                :     struct fmt_tm *tm;
                               3981                 :                :     int         thisdate;
                               3982                 :                : 
                               3983   [ +  -  +  +  :           3146 :     if (VARSIZE_ANY_EXHDR(fmt) <= 0 || TIMESTAMP_NOT_FINITE(dt))
                                              +  + ]
 9121 bruce@momjian.us         3984                 :             88 :         PG_RETURN_NULL();
                               3985                 :                : 
                               3986                 :           3058 :     ZERO_tmtc(&tmtc);
 7616 tgl@sss.pgh.pa.us        3987                 :           3058 :     tm = tmtcTm(&tmtc);
                               3988                 :                : 
 1608                          3989         [ -  + ]:           3058 :     if (timestamp2tm(dt, &tz, &tt, &tmtcFsec(&tmtc), &tmtcTzn(&tmtc), NULL) != 0)
 8432 tgl@sss.pgh.pa.us        3990         [ #  # ]:UBC           0 :         ereport(ERROR,
                               3991                 :                :                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                               3992                 :                :                  errmsg("timestamp out of range")));
                               3993                 :                : 
                               3994                 :                :     /* calculate wday and yday, because timestamp2tm doesn't */
 1607 tgl@sss.pgh.pa.us        3995                 :CBC        3058 :     thisdate = date2j(tt.tm_year, tt.tm_mon, tt.tm_mday);
                               3996                 :           3058 :     tt.tm_wday = (thisdate + 1) % 7;
                               3997                 :           3058 :     tt.tm_yday = thisdate - date2j(tt.tm_year, 1, 1) + 1;
                               3998                 :                : 
                               3999                 :           3058 :     COPY_tm(tm, &tt);
                               4000                 :                : 
 5679 peter_e@gmx.net          4001         [ -  + ]:           3058 :     if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION())))
 9121 bruce@momjian.us         4002                 :UBC           0 :         PG_RETURN_NULL();
                               4003                 :                : 
 9121 bruce@momjian.us         4004                 :CBC        3058 :     PG_RETURN_TEXT_P(res);
                               4005                 :                : }
                               4006                 :                : 
                               4007                 :                : 
                               4008                 :                : /*
                               4009                 :                :  * INTERVAL to_char()
                               4010                 :                :  */
                               4011                 :                : Datum
                               4012                 :            225 : interval_to_char(PG_FUNCTION_ARGS)
                               4013                 :                : {
 9072                          4014                 :            225 :     Interval   *it = PG_GETARG_INTERVAL_P(0);
 3455 noah@leadboat.com        4015                 :            225 :     text       *fmt = PG_GETARG_TEXT_PP(1),
                               4016                 :                :                *res;
                               4017                 :                :     TmToChar    tmtc;
                               4018                 :                :     struct fmt_tm *tm;
                               4019                 :                :     struct pg_itm tt,
 1608 tgl@sss.pgh.pa.us        4020                 :            225 :                *itm = &tt;
                               4021                 :                : 
 1017 dean.a.rasheed@gmail     4022   [ +  -  +  +  :            225 :     if (VARSIZE_ANY_EXHDR(fmt) <= 0 || INTERVAL_NOT_FINITE(it))
                                     +  -  -  +  +  
                                        +  +  -  +  
                                                 - ]
 9121 bruce@momjian.us         4023                 :              8 :         PG_RETURN_NULL();
                               4024                 :                : 
                               4025                 :            217 :     ZERO_tmtc(&tmtc);
 7616 tgl@sss.pgh.pa.us        4026                 :            217 :     tm = tmtcTm(&tmtc);
                               4027                 :                : 
 1608                          4028                 :            217 :     interval2itm(*it, itm);
                               4029                 :            217 :     tmtc.fsec = itm->tm_usec;
                               4030                 :            217 :     tm->tm_sec = itm->tm_sec;
                               4031                 :            217 :     tm->tm_min = itm->tm_min;
                               4032                 :            217 :     tm->tm_hour = itm->tm_hour;
                               4033                 :            217 :     tm->tm_mday = itm->tm_mday;
                               4034                 :            217 :     tm->tm_mon = itm->tm_mon;
                               4035                 :            217 :     tm->tm_year = itm->tm_year;
                               4036                 :                : 
                               4037                 :                :     /* wday is meaningless, yday approximates the total span in days */
 7616                          4038                 :            217 :     tm->tm_yday = (tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon) * DAYS_PER_MONTH + tm->tm_mday;
                               4039                 :                : 
 5679 peter_e@gmx.net          4040         [ -  + ]:            217 :     if (!(res = datetime_to_char_body(&tmtc, fmt, true, PG_GET_COLLATION())))
 9121 bruce@momjian.us         4041                 :UBC           0 :         PG_RETURN_NULL();
                               4042                 :                : 
 9121 bruce@momjian.us         4043                 :CBC         217 :     PG_RETURN_TEXT_P(res);
                               4044                 :                : }
                               4045                 :                : 
                               4046                 :                : /*
                               4047                 :                :  * TO_TIMESTAMP()
                               4048                 :                :  *
                               4049                 :                :  * Make Timestamp from date_str which is formatted at argument 'fmt'
                               4050                 :                :  * ( to_timestamp is reverse to_char() )
                               4051                 :                :  */
                               4052                 :                : Datum
 9553                          4053                 :            614 : to_timestamp(PG_FUNCTION_ARGS)
                               4054                 :                : {
 3455 noah@leadboat.com        4055                 :            614 :     text       *date_txt = PG_GETARG_TEXT_PP(0);
                               4056                 :            614 :     text       *fmt = PG_GETARG_TEXT_PP(1);
 2368 tgl@sss.pgh.pa.us        4057                 :            614 :     Oid         collid = PG_GET_COLLATION();
                               4058                 :                :     Timestamp   result;
                               4059                 :                :     int         tz;
                               4060                 :                :     struct pg_tm tm;
                               4061                 :                :     struct fmt_tz ftz;
                               4062                 :                :     fsec_t      fsec;
                               4063                 :                :     int         fprec;
                               4064                 :                : 
                               4065                 :            614 :     do_to_timestamp(date_txt, fmt, collid, false,
                               4066                 :                :                     &tm, &fsec, &ftz, &fprec, NULL, NULL);
                               4067                 :                : 
                               4068                 :                :     /* Use the specified time zone, if any. */
  945                          4069         [ +  + ]:            502 :     if (ftz.has_tz)
                               4070                 :             64 :         tz = ftz.gmtoffset;
                               4071                 :                :     else
 3152 andrew@dunslane.net      4072                 :            438 :         tz = DetermineTimeZoneOffset(&tm, session_timezone);
                               4073                 :                : 
 8403 tgl@sss.pgh.pa.us        4074         [ -  + ]:            502 :     if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
 8403 tgl@sss.pgh.pa.us        4075         [ #  # ]:UBC           0 :         ereport(ERROR,
                               4076                 :                :                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                               4077                 :                :                  errmsg("timestamp out of range")));
                               4078                 :                : 
                               4079                 :                :     /* Use the specified fractional precision, if any. */
 2537 akorotkov@postgresql     4080         [ +  + ]:CBC         502 :     if (fprec)
 1357 tgl@sss.pgh.pa.us        4081                 :            168 :         AdjustTimestampForTypmod(&result, fprec, NULL);
                               4082                 :                : 
 8403                          4083                 :            502 :     PG_RETURN_TIMESTAMP(result);
                               4084                 :                : }
                               4085                 :                : 
                               4086                 :                : /*
                               4087                 :                :  * TO_DATE
                               4088                 :                :  *  Make Date from date_str which is formatted at argument 'fmt'
                               4089                 :                :  */
                               4090                 :                : Datum
                               4091                 :            135 : to_date(PG_FUNCTION_ARGS)
                               4092                 :                : {
 3455 noah@leadboat.com        4093                 :            135 :     text       *date_txt = PG_GETARG_TEXT_PP(0);
                               4094                 :            135 :     text       *fmt = PG_GETARG_TEXT_PP(1);
 2368 tgl@sss.pgh.pa.us        4095                 :            135 :     Oid         collid = PG_GET_COLLATION();
                               4096                 :                :     DateADT     result;
                               4097                 :                :     struct pg_tm tm;
                               4098                 :                :     struct fmt_tz ftz;
                               4099                 :                :     fsec_t      fsec;
                               4100                 :                : 
                               4101                 :            135 :     do_to_timestamp(date_txt, fmt, collid, false,
                               4102                 :                :                     &tm, &fsec, &ftz, NULL, NULL, NULL);
                               4103                 :                : 
                               4104                 :                :     /* Prevent overflow in Julian-day routines */
 4973                          4105   [ -  +  -  -  :             94 :     if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
                                     -  -  -  +  -  
                                           -  -  - ]
 4973 tgl@sss.pgh.pa.us        4106         [ #  # ]:UBC           0 :         ereport(ERROR,
                               4107                 :                :                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                               4108                 :                :                  errmsg("date out of range: \"%s\"", text_to_cstring(date_txt))));
                               4109                 :                : 
 8403 tgl@sss.pgh.pa.us        4110                 :CBC          94 :     result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
                               4111                 :                : 
                               4112                 :                :     /* Now check for just-out-of-range dates */
 3816                          4113   [ +  -  -  + ]:             94 :     if (!IS_VALID_DATE(result))
 3816 tgl@sss.pgh.pa.us        4114         [ #  # ]:UBC           0 :         ereport(ERROR,
                               4115                 :                :                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                               4116                 :                :                  errmsg("date out of range: \"%s\"", text_to_cstring(date_txt))));
                               4117                 :                : 
 8403 tgl@sss.pgh.pa.us        4118                 :CBC          94 :     PG_RETURN_DATEADT(result);
                               4119                 :                : }
                               4120                 :                : 
                               4121                 :                : /*
                               4122                 :                :  * Convert the 'date_txt' input to a datetime type using argument 'fmt'
                               4123                 :                :  * as a format string.  The collation 'collid' may be used for case-folding
                               4124                 :                :  * rules in some cases.  'strict' specifies standard parsing mode.
                               4125                 :                :  *
                               4126                 :                :  * The actual data type (returned in 'typid', 'typmod') is determined by
                               4127                 :                :  * the presence of date/time/zone components in the format string.
                               4128                 :                :  *
                               4129                 :                :  * When a timezone component is present, the corresponding offset is
                               4130                 :                :  * returned in '*tz'.
                               4131                 :                :  *
                               4132                 :                :  * If escontext points to an ErrorSaveContext, data errors will be reported
                               4133                 :                :  * by filling that struct; the caller must test SOFT_ERROR_OCCURRED() to see
                               4134                 :                :  * whether an error occurred.  Otherwise, errors are thrown.
                               4135                 :                :  */
                               4136                 :                : Datum
 2368                          4137                 :          26244 : parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
                               4138                 :                :                Oid *typid, int32 *typmod, int *tz,
                               4139                 :                :                Node *escontext)
                               4140                 :                : {
                               4141                 :                :     struct pg_tm tm;
                               4142                 :                :     struct fmt_tz ftz;
                               4143                 :                :     fsec_t      fsec;
                               4144                 :                :     int         fprec;
                               4145                 :                :     uint32      flags;
                               4146                 :                : 
 1357                          4147         [ +  + ]:          26244 :     if (!do_to_timestamp(date_txt, fmt, collid, strict,
                               4148                 :                :                          &tm, &fsec, &ftz, &fprec, &flags, escontext))
                               4149                 :          20750 :         return (Datum) 0;
                               4150                 :                : 
 2528 akorotkov@postgresql     4151         [ -  + ]:           5454 :     *typmod = fprec ? fprec : -1;   /* fractional part precision */
                               4152                 :                : 
                               4153         [ +  + ]:           5454 :     if (flags & DCH_DATED)
                               4154                 :                :     {
                               4155         [ +  + ]:           3364 :         if (flags & DCH_TIMED)
                               4156                 :                :         {
                               4157         [ +  + ]:           2506 :             if (flags & DCH_ZONED)
                               4158                 :                :             {
                               4159                 :                :                 TimestampTz result;
                               4160                 :                : 
  945 tgl@sss.pgh.pa.us        4161         [ +  - ]:           1437 :                 if (ftz.has_tz)
                               4162                 :                :                 {
                               4163                 :           1437 :                     *tz = ftz.gmtoffset;
                               4164                 :                :                 }
                               4165                 :                :                 else
                               4166                 :                :                 {
                               4167                 :                :                     /*
                               4168                 :                :                      * Time zone is present in format string, but not in input
                               4169                 :                :                      * string.  Assuming do_to_timestamp() triggers no error
                               4170                 :                :                      * this should be possible only in non-strict case.
                               4171                 :                :                      */
 2528 akorotkov@postgresql     4172         [ #  # ]:UBC           0 :                     Assert(!strict);
                               4173                 :                : 
 1357 tgl@sss.pgh.pa.us        4174         [ #  # ]:              0 :                     ereturn(escontext, (Datum) 0,
                               4175                 :                :                             (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               4176                 :                :                              errmsg("missing time zone in input string for type timestamptz")));
                               4177                 :                :                 }
                               4178                 :                : 
 2528 akorotkov@postgresql     4179         [ -  + ]:CBC        1437 :                 if (tm2timestamp(&tm, fsec, tz, &result) != 0)
 1357 tgl@sss.pgh.pa.us        4180         [ #  # ]:UBC           0 :                     ereturn(escontext, (Datum) 0,
                               4181                 :                :                             (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                               4182                 :                :                              errmsg("timestamptz out of range")));
                               4183                 :                : 
 1357 tgl@sss.pgh.pa.us        4184                 :CBC        1437 :                 AdjustTimestampForTypmod(&result, *typmod, escontext);
                               4185                 :                : 
 2528 akorotkov@postgresql     4186                 :           1437 :                 *typid = TIMESTAMPTZOID;
                               4187                 :           1437 :                 return TimestampTzGetDatum(result);
                               4188                 :                :             }
                               4189                 :                :             else
                               4190                 :                :             {
                               4191                 :                :                 Timestamp   result;
                               4192                 :                : 
                               4193         [ -  + ]:           1069 :                 if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
 1357 tgl@sss.pgh.pa.us        4194         [ #  # ]:UBC           0 :                     ereturn(escontext, (Datum) 0,
                               4195                 :                :                             (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                               4196                 :                :                              errmsg("timestamp out of range")));
                               4197                 :                : 
 1357 tgl@sss.pgh.pa.us        4198                 :CBC        1069 :                 AdjustTimestampForTypmod(&result, *typmod, escontext);
                               4199                 :                : 
 2528 akorotkov@postgresql     4200                 :           1069 :                 *typid = TIMESTAMPOID;
                               4201                 :           1069 :                 return TimestampGetDatum(result);
                               4202                 :                :             }
                               4203                 :                :         }
                               4204                 :                :         else
                               4205                 :                :         {
                               4206         [ -  + ]:            858 :             if (flags & DCH_ZONED)
                               4207                 :                :             {
 1357 tgl@sss.pgh.pa.us        4208         [ #  # ]:UBC           0 :                 ereturn(escontext, (Datum) 0,
                               4209                 :                :                         (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               4210                 :                :                          errmsg("datetime format is zoned but not timed")));
                               4211                 :                :             }
                               4212                 :                :             else
                               4213                 :                :             {
                               4214                 :                :                 DateADT     result;
                               4215                 :                : 
                               4216                 :                :                 /* Prevent overflow in Julian-day routines */
 2528 akorotkov@postgresql     4217   [ -  +  -  -  :CBC         858 :                 if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
                                     -  -  -  +  -  
                                           -  -  - ]
 1357 tgl@sss.pgh.pa.us        4218         [ #  # ]:UBC           0 :                     ereturn(escontext, (Datum) 0,
                               4219                 :                :                             (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                               4220                 :                :                              errmsg("date out of range: \"%s\"", text_to_cstring(date_txt))));
                               4221                 :                : 
 2528 akorotkov@postgresql     4222                 :CBC         858 :                 result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) -
                               4223                 :                :                     POSTGRES_EPOCH_JDATE;
                               4224                 :                : 
                               4225                 :                :                 /* Now check for just-out-of-range dates */
                               4226   [ +  -  -  + ]:            858 :                 if (!IS_VALID_DATE(result))
 1357 tgl@sss.pgh.pa.us        4227         [ #  # ]:UBC           0 :                     ereturn(escontext, (Datum) 0,
                               4228                 :                :                             (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                               4229                 :                :                              errmsg("date out of range: \"%s\"", text_to_cstring(date_txt))));
                               4230                 :                : 
 2528 akorotkov@postgresql     4231                 :CBC         858 :                 *typid = DATEOID;
                               4232                 :            858 :                 return DateADTGetDatum(result);
                               4233                 :                :             }
                               4234                 :                :         }
                               4235                 :                :     }
                               4236         [ +  - ]:           2090 :     else if (flags & DCH_TIMED)
                               4237                 :                :     {
                               4238         [ +  + ]:           2090 :         if (flags & DCH_ZONED)
                               4239                 :                :         {
  260 michael@paquier.xyz      4240                 :           1181 :             TimeTzADT  *result = palloc_object(TimeTzADT);
                               4241                 :                : 
  945 tgl@sss.pgh.pa.us        4242         [ +  - ]:           1181 :             if (ftz.has_tz)
                               4243                 :                :             {
                               4244                 :           1181 :                 *tz = ftz.gmtoffset;
                               4245                 :                :             }
                               4246                 :                :             else
                               4247                 :                :             {
                               4248                 :                :                 /*
                               4249                 :                :                  * Time zone is present in format string, but not in input
                               4250                 :                :                  * string.  Assuming do_to_timestamp() triggers no error this
                               4251                 :                :                  * should be possible only in non-strict case.
                               4252                 :                :                  */
 2528 akorotkov@postgresql     4253         [ #  # ]:UBC           0 :                 Assert(!strict);
                               4254                 :                : 
 1357 tgl@sss.pgh.pa.us        4255         [ #  # ]:              0 :                 ereturn(escontext, (Datum) 0,
                               4256                 :                :                         (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               4257                 :                :                          errmsg("missing time zone in input string for type timetz")));
                               4258                 :                :             }
                               4259                 :                : 
 2528 akorotkov@postgresql     4260         [ -  + ]:CBC        1181 :             if (tm2timetz(&tm, fsec, *tz, result) != 0)
 1357 tgl@sss.pgh.pa.us        4261         [ #  # ]:UBC           0 :                 ereturn(escontext, (Datum) 0,
                               4262                 :                :                         (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                               4263                 :                :                          errmsg("timetz out of range")));
                               4264                 :                : 
 2528 akorotkov@postgresql     4265                 :CBC        1181 :             AdjustTimeForTypmod(&result->time, *typmod);
                               4266                 :                : 
                               4267                 :           1181 :             *typid = TIMETZOID;
                               4268                 :           1181 :             return TimeTzADTPGetDatum(result);
                               4269                 :                :         }
                               4270                 :                :         else
                               4271                 :                :         {
                               4272                 :                :             TimeADT     result;
                               4273                 :                : 
                               4274         [ -  + ]:            909 :             if (tm2time(&tm, fsec, &result) != 0)
 1357 tgl@sss.pgh.pa.us        4275         [ #  # ]:UBC           0 :                 ereturn(escontext, (Datum) 0,
                               4276                 :                :                         (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                               4277                 :                :                          errmsg("time out of range")));
                               4278                 :                : 
 2528 akorotkov@postgresql     4279                 :CBC         909 :             AdjustTimeForTypmod(&result, *typmod);
                               4280                 :                : 
                               4281                 :            909 :             *typid = TIMEOID;
                               4282                 :            909 :             return TimeADTGetDatum(result);
                               4283                 :                :         }
                               4284                 :                :     }
                               4285                 :                :     else
                               4286                 :                :     {
 1357 tgl@sss.pgh.pa.us        4287         [ #  # ]:UBC           0 :         ereturn(escontext, (Datum) 0,
                               4288                 :                :                 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               4289                 :                :                  errmsg("datetime format is not dated and not timed")));
                               4290                 :                :     }
                               4291                 :                : }
                               4292                 :                : 
                               4293                 :                : /*
                               4294                 :                :  * Parses the datetime format string in 'fmt_str' and returns true if it
                               4295                 :                :  * contains a timezone specifier, false if not.
                               4296                 :                :  */
                               4297                 :                : bool
  889 amitlan@postgresql.o     4298                 :CBC          44 : datetime_format_has_tz(const char *fmt_str)
                               4299                 :                : {
                               4300                 :                :     bool        incache;
  302 peter@eisentraut.org     4301                 :             44 :     size_t      fmt_len = strlen(fmt_str);
                               4302                 :                :     int         result;
                               4303                 :                :     FormatNode *format;
                               4304                 :                : 
  889 amitlan@postgresql.o     4305         [ -  + ]:             44 :     if (fmt_len > DCH_CACHE_SIZE)
                               4306                 :                :     {
                               4307                 :                :         /*
                               4308                 :                :          * Allocate new memory if format picture is bigger than static cache
                               4309                 :                :          * and do not use cache (call parser always)
                               4310                 :                :          */
  889 amitlan@postgresql.o     4311                 :UBC           0 :         incache = false;
                               4312                 :                : 
  108 nathan@postgresql.or     4313                 :              0 :         format = palloc_array(FormatNode, fmt_len + 1);
                               4314                 :                : 
  889 amitlan@postgresql.o     4315                 :              0 :         parse_format(format, fmt_str, DCH_keywords,
                               4316                 :                :                      DCH_suff, DCH_index, DCH_FLAG, NULL);
                               4317                 :                :     }
                               4318                 :                :     else
                               4319                 :                :     {
                               4320                 :                :         /*
                               4321                 :                :          * Use cache buffers
                               4322                 :                :          */
  889 amitlan@postgresql.o     4323                 :CBC          44 :         DCHCacheEntry *ent = DCH_cache_fetch(fmt_str, false);
                               4324                 :                : 
                               4325                 :             44 :         incache = true;
                               4326                 :             44 :         format = ent->format;
                               4327                 :                :     }
                               4328                 :                : 
                               4329                 :             44 :     result = DCH_datetime_type(format);
                               4330                 :                : 
                               4331         [ -  + ]:             44 :     if (!incache)
  889 amitlan@postgresql.o     4332                 :UBC           0 :         pfree(format);
                               4333                 :                : 
  889 amitlan@postgresql.o     4334                 :CBC          44 :     return result & DCH_ZONED;
                               4335                 :                : }
                               4336                 :                : 
                               4337                 :                : /*
                               4338                 :                :  * do_to_timestamp: shared code for to_timestamp and to_date
                               4339                 :                :  *
                               4340                 :                :  * Parse the 'date_txt' according to 'fmt', return results as a struct pg_tm,
                               4341                 :                :  * fractional seconds, struct fmt_tz, and fractional precision.
                               4342                 :                :  *
                               4343                 :                :  * 'collid' identifies the collation to use, if needed.
                               4344                 :                :  * 'std' specifies standard parsing mode.
                               4345                 :                :  *
                               4346                 :                :  * Bit mask of date/time/zone components found in 'fmt' is returned in 'flags',
                               4347                 :                :  * if that is not NULL.
                               4348                 :                :  *
                               4349                 :                :  * Returns true on success, false on failure (if escontext points to an
                               4350                 :                :  * ErrorSaveContext; otherwise errors are thrown).  Note that currently,
                               4351                 :                :  * soft-error behavior is provided for bad data but not bad format.
                               4352                 :                :  *
                               4353                 :                :  * We parse 'fmt' into a list of FormatNodes, which is then passed to
                               4354                 :                :  * DCH_from_char to populate a TmFromChar with the parsed contents of
                               4355                 :                :  * 'date_txt'.
                               4356                 :                :  *
                               4357                 :                :  * The TmFromChar is then analysed and converted into the final results in
                               4358                 :                :  * struct 'tm', 'fsec', struct 'tz', and 'fprec'.
                               4359                 :                :  */
                               4360                 :                : static bool
  302 peter@eisentraut.org     4361                 :          26993 : do_to_timestamp(const text *date_txt, const text *fmt, Oid collid, bool std,
                               4362                 :                :                 struct pg_tm *tm, fsec_t *fsec, struct fmt_tz *tz,
                               4363                 :                :                 int *fprec, uint32 *flags, Node *escontext)
                               4364                 :                : {
 2528 akorotkov@postgresql     4365                 :          26993 :     FormatNode *format = NULL;
  301 peter@eisentraut.org     4366                 :          26993 :     TmFromChar  tmfc = {0};
                               4367                 :                :     int         fmt_len;
                               4368                 :                :     char       *date_str;
                               4369                 :                :     int         fmask;
 2528 akorotkov@postgresql     4370                 :          26993 :     bool        incache = false;
                               4371                 :                : 
 2451 michael@paquier.xyz      4372         [ -  + ]:          26993 :     Assert(tm != NULL);
                               4373         [ -  + ]:          26993 :     Assert(fsec != NULL);
                               4374                 :                : 
 3620 tgl@sss.pgh.pa.us        4375                 :          26993 :     date_str = text_to_cstring(date_txt);
                               4376                 :                : 
 8403                          4377                 :          26993 :     ZERO_tm(tm);
                               4378                 :          26993 :     *fsec = 0;
  945                          4379                 :          26993 :     tz->has_tz = false;
 2451 michael@paquier.xyz      4380         [ +  + ]:          26993 :     if (fprec)
                               4381                 :          26858 :         *fprec = 0;
                               4382         [ +  + ]:          26993 :     if (flags)
                               4383                 :          26244 :         *flags = 0;
 3620 tgl@sss.pgh.pa.us        4384                 :          26993 :     fmask = 0;                  /* bit mask for ValidateDate() */
                               4385                 :                : 
 6729                          4386                 :          26993 :     fmt_len = VARSIZE_ANY_EXHDR(fmt);
                               4387                 :                : 
 8394                          4388         [ +  - ]:          26993 :     if (fmt_len)
                               4389                 :                :     {
                               4390                 :                :         char       *fmt_str;
                               4391                 :                : 
 6729                          4392                 :          26993 :         fmt_str = text_to_cstring(fmt);
                               4393                 :                : 
 8394                          4394         [ -  + ]:          26993 :         if (fmt_len > DCH_CACHE_SIZE)
                               4395                 :                :         {
                               4396                 :                :             /*
                               4397                 :                :              * Allocate new memory if format picture is bigger than static
                               4398                 :                :              * cache and do not use cache (call parser always)
                               4399                 :                :              */
  108 nathan@postgresql.or     4400                 :UBC           0 :             format = palloc_array(FormatNode, fmt_len + 1);
                               4401                 :                : 
 2528 akorotkov@postgresql     4402         [ #  # ]:              0 :             parse_format(format, fmt_str, DCH_keywords, DCH_suff, DCH_index,
                               4403                 :                :                          DCH_FLAG | (std ? STD_FLAG : 0), NULL);
                               4404                 :                :         }
                               4405                 :                :         else
                               4406                 :                :         {
                               4407                 :                :             /*
                               4408                 :                :              * Use cache buffers
                               4409                 :                :              */
 2528 akorotkov@postgresql     4410                 :CBC       26993 :             DCHCacheEntry *ent = DCH_cache_fetch(fmt_str, std);
                               4411                 :                : 
 3298 peter_e@gmx.net          4412                 :          26989 :             incache = true;
 9633 bruce@momjian.us         4413                 :          26989 :             format = ent->format;
                               4414                 :                :         }
                               4415                 :                : 
                               4416                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               4417                 :                :         /* dump_node(format, fmt_len); */
                               4418                 :                :         /* dump_index(DCH_keywords, DCH_index); */
                               4419                 :                : #endif
                               4420                 :                : 
 1357 tgl@sss.pgh.pa.us        4421                 :          26989 :         DCH_from_char(format, date_str, &tmfc, collid, std, escontext);
 8394                          4422                 :          26888 :         pfree(fmt_str);
 1357                          4423   [ +  +  +  -  :          26888 :         if (SOFT_ERROR_OCCURRED(escontext))
                                              +  + ]
                               4424                 :          20750 :             goto fail;
                               4425                 :                : 
 2528 akorotkov@postgresql     4426         [ +  + ]:           6138 :         if (flags)
 1357 tgl@sss.pgh.pa.us        4427                 :           5454 :             *flags = DCH_datetime_type(format);
                               4428                 :                : 
 8394                          4429         [ -  + ]:           6138 :         if (!incache)
                               4430                 :                :         {
 9711 bruce@momjian.us         4431                 :UBC           0 :             pfree(format);
 2528 akorotkov@postgresql     4432                 :              0 :             format = NULL;
                               4433                 :                :         }
                               4434                 :                :     }
                               4435                 :                : 
                               4436                 :                :     DEBUG_TMFC(&tmfc);
                               4437                 :                : 
                               4438                 :                :     /*
                               4439                 :                :      * Convert to_date/to_timestamp input fields to standard 'tm'
                               4440                 :                :      */
 9121 bruce@momjian.us         4441         [ +  + ]:CBC        6138 :     if (tmfc.ssss)
                               4442                 :                :     {
 9072                          4443                 :             16 :         int         x = tmfc.ssss;
                               4444                 :                : 
 7707                          4445                 :             16 :         tm->tm_hour = x / SECS_PER_HOUR;
                               4446                 :             16 :         x %= SECS_PER_HOUR;
                               4447                 :             16 :         tm->tm_min = x / SECS_PER_MINUTE;
                               4448                 :             16 :         x %= SECS_PER_MINUTE;
 8403 tgl@sss.pgh.pa.us        4449                 :             16 :         tm->tm_sec = x;
                               4450                 :                :     }
                               4451                 :                : 
 9121 bruce@momjian.us         4452         [ +  + ]:           6138 :     if (tmfc.ss)
 8403 tgl@sss.pgh.pa.us        4453                 :            908 :         tm->tm_sec = tmfc.ss;
 9121 bruce@momjian.us         4454         [ +  + ]:           6138 :     if (tmfc.mi)
 8403 tgl@sss.pgh.pa.us        4455                 :           4852 :         tm->tm_min = tmfc.mi;
 9121 bruce@momjian.us         4456         [ +  + ]:           6138 :     if (tmfc.hh)
 8403 tgl@sss.pgh.pa.us        4457                 :           4896 :         tm->tm_hour = tmfc.hh;
                               4458                 :                : 
  300 peter@eisentraut.org     4459         [ +  + ]:           6138 :     if (tmfc.clock_12_hour)
                               4460                 :                :     {
 5647 bruce@momjian.us         4461   [ +  -  +  + ]:             80 :         if (tm->tm_hour < 1 || tm->tm_hour > HOURS_PER_DAY / 2)
                               4462                 :                :         {
 1357 tgl@sss.pgh.pa.us        4463         [ +  - ]:              4 :             errsave(escontext,
                               4464                 :                :                     (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               4465                 :                :                      errmsg("hour \"%d\" is invalid for the 12-hour clock", tm->tm_hour),
                               4466                 :                :                      errhint("Use the 24-hour clock, or give an hour between 1 and 12.")));
 1357 tgl@sss.pgh.pa.us        4467                 :UBC           0 :             goto fail;
                               4468                 :                :         }
                               4469                 :                : 
 5647 bruce@momjian.us         4470   [ +  +  +  - ]:CBC          76 :         if (tmfc.pm && tm->tm_hour < HOURS_PER_DAY / 2)
                               4471                 :              8 :             tm->tm_hour += HOURS_PER_DAY / 2;
                               4472   [ +  -  -  + ]:             68 :         else if (!tmfc.pm && tm->tm_hour == HOURS_PER_DAY / 2)
 8403 tgl@sss.pgh.pa.us        4473                 :UBC           0 :             tm->tm_hour = 0;
                               4474                 :                :     }
                               4475                 :                : 
 6559 tgl@sss.pgh.pa.us        4476         [ +  + ]:CBC        6134 :     if (tmfc.year)
                               4477                 :                :     {
                               4478                 :                :         /*
                               4479                 :                :          * If CC and YY (or Y) are provided, use YY as 2 low-order digits for
                               4480                 :                :          * the year in the given century.  Keep in mind that the 21st century
                               4481                 :                :          * AD runs from 2001-2100, not 2000-2099; 6th century BC runs from
                               4482                 :                :          * 600BC to 501BC.
                               4483                 :                :          */
 7167                          4484   [ +  +  +  - ]:           4020 :         if (tmfc.cc && tmfc.yysz <= 2)
                               4485                 :                :         {
 5133 bruce@momjian.us         4486         [ -  + ]:             12 :             if (tmfc.bc)
 5133 bruce@momjian.us         4487                 :UBC           0 :                 tmfc.cc = -tmfc.cc;
 6559 tgl@sss.pgh.pa.us        4488                 :CBC          12 :             tm->tm_year = tmfc.year % 100;
 7167                          4489         [ +  - ]:             12 :             if (tm->tm_year)
                               4490                 :                :             {
                               4491                 :                :                 int         tmp;
                               4492                 :                : 
 5133 bruce@momjian.us         4493         [ +  + ]:             12 :                 if (tmfc.cc >= 0)
                               4494                 :                :                 {
                               4495                 :                :                     /* tm->tm_year += (tmfc.cc - 1) * 100; */
  626 nathan@postgresql.or     4496                 :              8 :                     tmp = tmfc.cc - 1;
                               4497   [ +  +  -  + ]:             12 :                     if (pg_mul_s32_overflow(tmp, 100, &tmp) ||
                               4498                 :              4 :                         pg_add_s32_overflow(tm->tm_year, tmp, &tm->tm_year))
                               4499                 :                :                     {
                               4500                 :              4 :                         DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
                               4501                 :              4 :                                            text_to_cstring(date_txt), "timestamp",
                               4502                 :                :                                            escontext);
  626 nathan@postgresql.or     4503                 :UBC           0 :                         goto fail;
                               4504                 :                :                     }
                               4505                 :                :                 }
                               4506                 :                :                 else
                               4507                 :                :                 {
                               4508                 :                :                     /* tm->tm_year = (tmfc.cc + 1) * 100 - tm->tm_year + 1; */
  626 nathan@postgresql.or     4509                 :CBC           4 :                     tmp = tmfc.cc + 1;
                               4510   [ -  +  -  - ]:              4 :                     if (pg_mul_s32_overflow(tmp, 100, &tmp) ||
  626 nathan@postgresql.or     4511         [ #  # ]:UBC           0 :                         pg_sub_s32_overflow(tmp, tm->tm_year, &tmp) ||
                               4512                 :              0 :                         pg_add_s32_overflow(tmp, 1, &tm->tm_year))
                               4513                 :                :                     {
  626 nathan@postgresql.or     4514                 :CBC           4 :                         DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
                               4515                 :              4 :                                            text_to_cstring(date_txt), "timestamp",
                               4516                 :                :                                            escontext);
  626 nathan@postgresql.or     4517                 :UBC           0 :                         goto fail;
                               4518                 :                :                     }
                               4519                 :                :                 }
                               4520                 :                :             }
                               4521                 :                :             else
                               4522                 :                :             {
                               4523                 :                :                 /* find century year for dates ending in "00" */
 4838 bruce@momjian.us         4524                 :              0 :                 tm->tm_year = tmfc.cc * 100 + ((tmfc.cc >= 0) ? 0 : 1);
                               4525                 :                :             }
                               4526                 :                :         }
                               4527                 :                :         else
                               4528                 :                :         {
                               4529                 :                :             /* If a 4-digit year is provided, we use that and ignore CC. */
 6559 tgl@sss.pgh.pa.us        4530                 :CBC        4008 :             tm->tm_year = tmfc.year;
 2157                          4531         [ +  + ]:           4008 :             if (tmfc.bc)
                               4532                 :             24 :                 tm->tm_year = -tm->tm_year;
                               4533                 :                :             /* correct for our representation of BC years */
                               4534         [ +  + ]:           4008 :             if (tm->tm_year < 0)
                               4535                 :             24 :                 tm->tm_year++;
                               4536                 :                :         }
 3620                          4537                 :           4012 :         fmask |= DTK_M(YEAR);
                               4538                 :                :     }
                               4539         [ +  + ]:           2114 :     else if (tmfc.cc)
                               4540                 :                :     {
                               4541                 :                :         /* use first year of century */
 5133 bruce@momjian.us         4542         [ -  + ]:              8 :         if (tmfc.bc)
 5133 bruce@momjian.us         4543                 :UBC           0 :             tmfc.cc = -tmfc.cc;
 5133 bruce@momjian.us         4544         [ +  + ]:CBC           8 :         if (tmfc.cc >= 0)
                               4545                 :                :         {
                               4546                 :                :             /* +1 because 21st century started in 2001 */
                               4547                 :                :             /* tm->tm_year = (tmfc.cc - 1) * 100 + 1; */
  626 nathan@postgresql.or     4548   [ -  +  -  - ]:              4 :             if (pg_mul_s32_overflow(tmfc.cc - 1, 100, &tm->tm_year) ||
  626 nathan@postgresql.or     4549                 :UBC           0 :                 pg_add_s32_overflow(tm->tm_year, 1, &tm->tm_year))
                               4550                 :                :             {
  626 nathan@postgresql.or     4551                 :CBC           4 :                 DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
                               4552                 :              4 :                                    text_to_cstring(date_txt), "timestamp",
                               4553                 :                :                                    escontext);
  626 nathan@postgresql.or     4554                 :UBC           0 :                 goto fail;
                               4555                 :                :             }
                               4556                 :                :         }
                               4557                 :                :         else
                               4558                 :                :         {
                               4559                 :                :             /* +1 because year == 599 is 600 BC */
                               4560                 :                :             /* tm->tm_year = tmfc.cc * 100 + 1; */
  626 nathan@postgresql.or     4561   [ -  +  -  - ]:CBC           4 :             if (pg_mul_s32_overflow(tmfc.cc, 100, &tm->tm_year) ||
  626 nathan@postgresql.or     4562                 :UBC           0 :                 pg_add_s32_overflow(tm->tm_year, 1, &tm->tm_year))
                               4563                 :                :             {
  626 nathan@postgresql.or     4564                 :CBC           4 :                 DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
                               4565                 :              4 :                                    text_to_cstring(date_txt), "timestamp",
                               4566                 :                :                                    escontext);
  626 nathan@postgresql.or     4567                 :UBC           0 :                 goto fail;
                               4568                 :                :             }
                               4569                 :                :         }
 3620 tgl@sss.pgh.pa.us        4570                 :              0 :         fmask |= DTK_M(YEAR);
                               4571                 :                :     }
                               4572                 :                : 
 9121 bruce@momjian.us         4573         [ +  + ]:CBC        6118 :     if (tmfc.j)
                               4574                 :                :     {
 8403 tgl@sss.pgh.pa.us        4575                 :              4 :         j2date(tmfc.j, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
 3620                          4576                 :              4 :         fmask |= DTK_DATE_M;
                               4577                 :                :     }
                               4578                 :                : 
 6559                          4579         [ +  + ]:           6118 :     if (tmfc.ww)
                               4580                 :                :     {
                               4581         [ +  + ]:             24 :         if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
                               4582                 :                :         {
                               4583                 :                :             /*
                               4584                 :                :              * If tmfc.d is not set, then the date is left at the beginning of
                               4585                 :                :              * the ISO week (Monday).
                               4586                 :                :              */
                               4587         [ +  - ]:             16 :             if (tmfc.d)
                               4588                 :             16 :                 isoweekdate2date(tmfc.ww, tmfc.d, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
                               4589                 :                :             else
 6559 tgl@sss.pgh.pa.us        4590                 :UBC           0 :                 isoweek2date(tmfc.ww, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
 3620 tgl@sss.pgh.pa.us        4591                 :CBC          16 :             fmask |= DTK_DATE_M;
                               4592                 :                :         }
                               4593                 :                :         else
                               4594                 :                :         {
                               4595                 :                :             /* tmfc.ddd = (tmfc.ww - 1) * 7 + 1; */
  626 nathan@postgresql.or     4596   [ +  -  +  + ]:             16 :             if (pg_sub_s32_overflow(tmfc.ww, 1, &tmfc.ddd) ||
                               4597         [ -  + ]:             12 :                 pg_mul_s32_overflow(tmfc.ddd, 7, &tmfc.ddd) ||
                               4598                 :              4 :                 pg_add_s32_overflow(tmfc.ddd, 1, &tmfc.ddd))
                               4599                 :                :             {
                               4600                 :              4 :                 DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
                               4601                 :                :                                    date_str, "timestamp", escontext);
  626 nathan@postgresql.or     4602                 :UBC           0 :                 goto fail;
                               4603                 :                :             }
                               4604                 :                :         }
                               4605                 :                :     }
                               4606                 :                : 
 6559 tgl@sss.pgh.pa.us        4607         [ +  + ]:CBC        6114 :     if (tmfc.w)
                               4608                 :                :     {
                               4609                 :                :         /* tmfc.dd = (tmfc.w - 1) * 7 + 1; */
  626 nathan@postgresql.or     4610   [ +  -  +  + ]:             16 :         if (pg_sub_s32_overflow(tmfc.w, 1, &tmfc.dd) ||
                               4611         [ -  + ]:             12 :             pg_mul_s32_overflow(tmfc.dd, 7, &tmfc.dd) ||
                               4612                 :              4 :             pg_add_s32_overflow(tmfc.dd, 1, &tmfc.dd))
                               4613                 :                :         {
                               4614                 :              4 :             DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
                               4615                 :                :                                date_str, "timestamp", escontext);
  626 nathan@postgresql.or     4616                 :UBC           0 :             goto fail;
                               4617                 :                :         }
                               4618                 :                :     }
 9121 bruce@momjian.us         4619         [ +  + ]:CBC        6110 :     if (tmfc.dd)
                               4620                 :                :     {
 8403 tgl@sss.pgh.pa.us        4621                 :           3920 :         tm->tm_mday = tmfc.dd;
 3620                          4622                 :           3920 :         fmask |= DTK_M(DAY);
                               4623                 :                :     }
 9121 bruce@momjian.us         4624         [ +  + ]:           6110 :     if (tmfc.mm)
                               4625                 :                :     {
 8403 tgl@sss.pgh.pa.us        4626                 :           3944 :         tm->tm_mon = tmfc.mm;
 3620                          4627                 :           3944 :         fmask |= DTK_M(MONTH);
                               4628                 :                :     }
                               4629                 :                : 
 8403                          4630   [ +  +  -  +  :           6110 :     if (tmfc.ddd && (tm->tm_mon <= 1 || tm->tm_mday <= 1))
                                              -  - ]
                               4631                 :                :     {
                               4632                 :                :         /*
                               4633                 :                :          * The month and day field have not been set, so we use the
                               4634                 :                :          * day-of-year field to populate them.  Depending on the date mode,
                               4635                 :                :          * this field may be interpreted as a Gregorian day-of-year, or an ISO
                               4636                 :                :          * week date day-of-year.
                               4637                 :                :          */
                               4638                 :                : 
 6374                          4639   [ -  +  -  - ]:             32 :         if (!tm->tm_year && !tmfc.bc)
                               4640                 :                :         {
 1357 tgl@sss.pgh.pa.us        4641         [ #  # ]:UBC           0 :             errsave(escontext,
                               4642                 :                :                     (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                               4643                 :                :                      errmsg("cannot calculate day of year without year information")));
                               4644                 :              0 :             goto fail;
                               4645                 :                :         }
                               4646                 :                : 
 6559 tgl@sss.pgh.pa.us        4647         [ +  + ]:CBC          32 :         if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
                               4648                 :                :         {
                               4649                 :                :             int         j0;     /* zeroth day of the ISO year, in Julian */
                               4650                 :                : 
 6374                          4651                 :              4 :             j0 = isoweek2j(tm->tm_year, 1) - 1;
                               4652                 :                : 
 7132 bruce@momjian.us         4653                 :              4 :             j2date(j0 + tmfc.ddd, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
 3620 tgl@sss.pgh.pa.us        4654                 :              4 :             fmask |= DTK_DATE_M;
                               4655                 :                :         }
                               4656                 :                :         else
                               4657                 :                :         {
                               4658                 :                :             const int  *y;
                               4659                 :                :             int         i;
                               4660                 :                : 
                               4661                 :                :             static const int ysum[2][13] = {
                               4662                 :                :                 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
                               4663                 :                :             {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};
                               4664                 :                : 
 7132 bruce@momjian.us         4665   [ +  +  -  +  :             28 :             y = ysum[isleap(tm->tm_year)];
                                              -  - ]
                               4666                 :                : 
 5647                          4667         [ +  + ]:            328 :             for (i = 1; i <= MONTHS_PER_YEAR; i++)
                               4668                 :                :             {
 3620 tgl@sss.pgh.pa.us        4669         [ +  + ]:            320 :                 if (tmfc.ddd <= y[i])
 7132 bruce@momjian.us         4670                 :             20 :                     break;
                               4671                 :                :             }
                               4672         [ +  - ]:             28 :             if (tm->tm_mon <= 1)
 6374 tgl@sss.pgh.pa.us        4673                 :             28 :                 tm->tm_mon = i;
                               4674                 :                : 
 7132 bruce@momjian.us         4675         [ +  - ]:             28 :             if (tm->tm_mday <= 1)
 6374 tgl@sss.pgh.pa.us        4676                 :             28 :                 tm->tm_mday = tmfc.ddd - y[i - 1];
                               4677                 :                : 
 3620                          4678                 :             28 :             fmask |= DTK_M(MONTH) | DTK_M(DAY);
                               4679                 :                :         }
                               4680                 :                :     }
                               4681                 :                : 
 8894 lockhart@fourpalms.o     4682         [ +  + ]:           6110 :     if (tmfc.ms)
                               4683                 :                :     {
  626 nathan@postgresql.or     4684                 :              8 :         int         tmp = 0;
                               4685                 :                : 
                               4686                 :                :         /* *fsec += tmfc.ms * 1000; */
                               4687   [ +  +  -  + ]:             12 :         if (pg_mul_s32_overflow(tmfc.ms, 1000, &tmp) ||
                               4688                 :              4 :             pg_add_s32_overflow(*fsec, tmp, fsec))
                               4689                 :                :         {
                               4690                 :              4 :             DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
                               4691                 :                :                                date_str, "timestamp", escontext);
  626 nathan@postgresql.or     4692                 :UBC           0 :             goto fail;
                               4693                 :                :         }
                               4694                 :                :     }
 8894 lockhart@fourpalms.o     4695         [ +  + ]:CBC        6106 :     if (tmfc.us)
 8403 tgl@sss.pgh.pa.us        4696                 :            676 :         *fsec += tmfc.us;
 2537 akorotkov@postgresql     4697         [ +  + ]:           6106 :     if (fprec)
                               4698                 :           5992 :         *fprec = tmfc.ff;       /* fractional precision, if specified */
                               4699                 :                : 
                               4700                 :                :     /* Range-check date fields according to bit mask computed above */
 3620 tgl@sss.pgh.pa.us        4701         [ +  + ]:           6106 :     if (fmask != 0)
                               4702                 :                :     {
                               4703                 :                :         /* We already dealt with AD/BC, so pass isjulian = true */
                               4704                 :           4016 :         int         dterr = ValidateDate(fmask, true, false, false, tm);
                               4705                 :                : 
                               4706         [ +  + ]:           4016 :         if (dterr != 0)
                               4707                 :                :         {
                               4708                 :                :             /*
                               4709                 :                :              * Force the error to be DTERR_FIELD_OVERFLOW even if ValidateDate
                               4710                 :                :              * said DTERR_MD_FIELD_OVERFLOW, because we don't want to print an
                               4711                 :                :              * irrelevant hint about datestyle.
                               4712                 :                :              */
 1357                          4713                 :             32 :             DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
                               4714                 :                :                                date_str, "timestamp", escontext);
 1357 tgl@sss.pgh.pa.us        4715                 :UBC           0 :             goto fail;
                               4716                 :                :         }
                               4717                 :                :     }
                               4718                 :                : 
                               4719                 :                :     /* Range-check time fields too */
 3620 tgl@sss.pgh.pa.us        4720   [ +  -  +  + ]:CBC        6074 :     if (tm->tm_hour < 0 || tm->tm_hour >= HOURS_PER_DAY ||
                               4721   [ +  -  +  + ]:           6062 :         tm->tm_min < 0 || tm->tm_min >= MINS_PER_HOUR ||
                               4722   [ +  -  +  + ]:           6058 :         tm->tm_sec < 0 || tm->tm_sec >= SECS_PER_MINUTE ||
 3472                          4723   [ +  -  +  + ]:           6054 :         *fsec < INT64CONST(0) || *fsec >= USECS_PER_SEC)
                               4724                 :                :     {
 1357                          4725                 :             24 :         DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
                               4726                 :                :                            date_str, "timestamp", escontext);
 1357 tgl@sss.pgh.pa.us        4727                 :UBC           0 :         goto fail;
                               4728                 :                :     }
                               4729                 :                : 
                               4730                 :                :     /*
                               4731                 :                :      * If timezone info was present, reduce it to a GMT offset.  (We cannot do
                               4732                 :                :      * this until we've filled all of the tm struct, since the zone's offset
                               4733                 :                :      * might be time-varying.)
                               4734                 :                :      */
 3152 andrew@dunslane.net      4735         [ +  + ]:CBC        6050 :     if (tmfc.tzsign)
                               4736                 :                :     {
                               4737                 :                :         /* TZH and/or TZM fields */
                               4738   [ +  -  +  - ]:           2658 :         if (tmfc.tzh < 0 || tmfc.tzh > MAX_TZDISP_HOUR ||
                               4739   [ +  -  -  + ]:           2658 :             tmfc.tzm < 0 || tmfc.tzm >= MINS_PER_HOUR)
                               4740                 :                :         {
 1357 tgl@sss.pgh.pa.us        4741                 :UBC           0 :             DateTimeParseError(DTERR_TZDISP_OVERFLOW, NULL,
                               4742                 :                :                                date_str, "timestamp", escontext);
                               4743                 :              0 :             goto fail;
                               4744                 :                :         }
                               4745                 :                : 
  945 tgl@sss.pgh.pa.us        4746                 :CBC        2658 :         tz->has_tz = true;
                               4747                 :           2658 :         tz->gmtoffset = (tmfc.tzh * MINS_PER_HOUR + tmfc.tzm) * SECS_PER_MINUTE;
                               4748                 :                :         /* note we are flipping the sign convention here */
                               4749         [ +  + ]:           2658 :         if (tmfc.tzsign > 0)
                               4750                 :           2426 :             tz->gmtoffset = -tz->gmtoffset;
                               4751                 :                :     }
                               4752         [ +  + ]:           3392 :     else if (tmfc.has_tz)
                               4753                 :                :     {
                               4754                 :                :         /* TZ field */
                               4755                 :             24 :         tz->has_tz = true;
                               4756         [ +  + ]:             24 :         if (tmfc.tzp == NULL)
                               4757                 :                :         {
                               4758                 :                :             /* fixed-offset abbreviation; flip the sign convention */
                               4759                 :             20 :             tz->gmtoffset = -tmfc.gmtoffset;
                               4760                 :                :         }
                               4761                 :                :         else
                               4762                 :                :         {
                               4763                 :                :             /* dynamic-offset abbreviation, resolve using specified time */
                               4764                 :              4 :             tz->gmtoffset = DetermineTimeZoneAbbrevOffset(tm, tmfc.abbrev,
                               4765                 :                :                                                           tmfc.tzp);
                               4766                 :                :         }
                               4767                 :                :     }
                               4768                 :                : 
                               4769                 :                :     DEBUG_TM(tm);
                               4770                 :                : 
 2528 akorotkov@postgresql     4771   [ +  -  -  + ]:           6050 :     if (format && !incache)
 2528 akorotkov@postgresql     4772                 :UBC           0 :         pfree(format);
 1357 tgl@sss.pgh.pa.us        4773                 :CBC        6050 :     pfree(date_str);
                               4774                 :                : 
                               4775                 :           6050 :     return true;
                               4776                 :                : 
                               4777                 :          20750 : fail:
                               4778   [ +  -  -  + ]:          20750 :     if (format && !incache)
 1357 tgl@sss.pgh.pa.us        4779                 :UBC           0 :         pfree(format);
 3620 tgl@sss.pgh.pa.us        4780                 :CBC       20750 :     pfree(date_str);
                               4781                 :                : 
 1357                          4782                 :          20750 :     return false;
                               4783                 :                : }
                               4784                 :                : 
                               4785                 :                : 
                               4786                 :                : /**********************************************************************
                               4787                 :                :  *  the NUMBER version part
                               4788                 :                :  *********************************************************************/
                               4789                 :                : 
                               4790                 :                : 
                               4791                 :                : /*
                               4792                 :                :  * Fill str with character c max times, and add terminating \0.  (So max+1
                               4793                 :                :  * bytes are written altogether!)
                               4794                 :                :  */
                               4795                 :                : static void
 9711 bruce@momjian.us         4796                 :            152 : fill_str(char *str, int c, int max)
                               4797                 :                : {
                               4798                 :            152 :     memset(str, c, max);
  302 peter@eisentraut.org     4799                 :            152 :     str[max] = '\0';
 9711 bruce@momjian.us         4800                 :            152 : }
                               4801                 :                : 
                               4802                 :                : /* This works the same as DCH_prevent_counter_overflow */
                               4803                 :                : static inline void
 2872 tgl@sss.pgh.pa.us        4804                 :         699753 : NUM_prevent_counter_overflow(void)
                               4805                 :                : {
                               4806         [ -  + ]:         699753 :     if (NUMCounter >= (INT_MAX - 1))
                               4807                 :                :     {
 2872 tgl@sss.pgh.pa.us        4808         [ #  # ]:UBC           0 :         for (int i = 0; i < n_NUMCache; i++)
                               4809                 :              0 :             NUMCache[i]->age >>= 1;
                               4810                 :              0 :         NUMCounter >>= 1;
                               4811                 :                :     }
 2872 tgl@sss.pgh.pa.us        4812                 :CBC      699753 : }
                               4813                 :                : 
                               4814                 :                : /* select a NUMCacheEntry to hold the given format picture */
                               4815                 :                : static NUMCacheEntry *
 3620                          4816                 :            409 : NUM_cache_getnew(const char *str)
                               4817                 :                : {
                               4818                 :                :     NUMCacheEntry *ent;
                               4819                 :                : 
                               4820                 :                :     /* Ensure we can advance NUMCounter below */
 2872                          4821                 :            409 :     NUM_prevent_counter_overflow();
                               4822                 :                : 
                               4823                 :                :     /*
                               4824                 :                :      * If cache is full, remove oldest entry (or recycle first not-valid one)
                               4825                 :                :      */
 3620                          4826         [ +  + ]:            409 :     if (n_NUMCache >= NUM_CACHE_ENTRIES)
                               4827                 :                :     {
 2872                          4828                 :            196 :         NUMCacheEntry *old = NUMCache[0];
                               4829                 :                : 
                               4830                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               4831                 :                :         elog(DEBUG_elog_output, "Cache is full (%d)", n_NUMCache);
                               4832                 :                : #endif
 3620                          4833         [ +  - ]:            196 :         if (old->valid)
                               4834                 :                :         {
 2872                          4835         [ +  + ]:           3852 :             for (int i = 1; i < NUM_CACHE_ENTRIES; i++)
                               4836                 :                :             {
                               4837                 :           3660 :                 ent = NUMCache[i];
 3620                          4838         [ +  + ]:           3660 :                 if (!ent->valid)
                               4839                 :                :                 {
                               4840                 :              4 :                     old = ent;
                               4841                 :              4 :                     break;
                               4842                 :                :                 }
                               4843         [ +  + ]:           3656 :                 if (ent->age < old->age)
                               4844                 :            196 :                     old = ent;
                               4845                 :                :             }
                               4846                 :                :         }
                               4847                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               4848                 :                :         elog(DEBUG_elog_output, "OLD: \"%s\" AGE: %d", old->str, old->age);
                               4849                 :                : #endif
                               4850                 :            196 :         old->valid = false;
 2208 peter@eisentraut.org     4851                 :            196 :         strlcpy(old->str, str, NUM_CACHE_SIZE + 1);
 9660 bruce@momjian.us         4852                 :            196 :         old->age = (++NUMCounter);
                               4853                 :                :         /* caller is expected to fill format and Num, then set valid */
 3620 tgl@sss.pgh.pa.us        4854                 :            196 :         return old;
                               4855                 :                :     }
                               4856                 :                :     else
                               4857                 :                :     {
                               4858                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               4859                 :                :         elog(DEBUG_elog_output, "NEW (%d)", n_NUMCache);
                               4860                 :                : #endif
 2872                          4861         [ -  + ]:            213 :         Assert(NUMCache[n_NUMCache] == NULL);
                               4862                 :            213 :         NUMCache[n_NUMCache] = ent = (NUMCacheEntry *)
                               4863                 :            213 :             MemoryContextAllocZero(TopMemoryContext, sizeof(NUMCacheEntry));
 3620                          4864                 :            213 :         ent->valid = false;
 2208 peter@eisentraut.org     4865                 :            213 :         strlcpy(ent->str, str, NUM_CACHE_SIZE + 1);
 9660 bruce@momjian.us         4866                 :            213 :         ent->age = (++NUMCounter);
                               4867                 :                :         /* caller is expected to fill format and Num, then set valid */
                               4868                 :            213 :         ++n_NUMCache;
 3620 tgl@sss.pgh.pa.us        4869                 :            213 :         return ent;
                               4870                 :                :     }
                               4871                 :                : }
                               4872                 :                : 
                               4873                 :                : /* look for an existing NUMCacheEntry matching the given format picture */
                               4874                 :                : static NUMCacheEntry *
                               4875                 :         699344 : NUM_cache_search(const char *str)
                               4876                 :                : {
                               4877                 :                :     /* Ensure we can advance NUMCounter below */
 2872                          4878                 :         699344 :     NUM_prevent_counter_overflow();
                               4879                 :                : 
                               4880         [ +  + ]:         878910 :     for (int i = 0; i < n_NUMCache; i++)
                               4881                 :                :     {
                               4882                 :         878501 :         NUMCacheEntry *ent = NUMCache[i];
                               4883                 :                : 
 3620                          4884   [ +  +  +  + ]:         878501 :         if (ent->valid && strcmp(ent->str, str) == 0)
                               4885                 :                :         {
 9660 bruce@momjian.us         4886                 :         698935 :             ent->age = (++NUMCounter);
                               4887                 :         698935 :             return ent;
                               4888                 :                :         }
                               4889                 :                :     }
                               4890                 :                : 
 8268 neilc@samurai.com        4891                 :            409 :     return NULL;
                               4892                 :                : }
                               4893                 :                : 
                               4894                 :                : /* Find or create a NUMCacheEntry for the given format picture */
                               4895                 :                : static NUMCacheEntry *
 3620 tgl@sss.pgh.pa.us        4896                 :         699344 : NUM_cache_fetch(const char *str)
                               4897                 :                : {
                               4898                 :                :     NUMCacheEntry *ent;
                               4899                 :                : 
                               4900         [ +  + ]:         699344 :     if ((ent = NUM_cache_search(str)) == NULL)
                               4901                 :                :     {
                               4902                 :                :         /*
                               4903                 :                :          * Not in the cache, must run parser and save a new format-picture to
                               4904                 :                :          * the cache.  Do not mark the cache entry valid until parsing
                               4905                 :                :          * succeeds.
                               4906                 :                :          */
                               4907                 :            409 :         ent = NUM_cache_getnew(str);
                               4908                 :                : 
  301 peter@eisentraut.org     4909                 :            409 :         memset(&ent->Num, 0, sizeof ent->Num);
                               4910                 :                : 
 3620 tgl@sss.pgh.pa.us        4911                 :            409 :         parse_format(ent->format, str, NUM_keywords,
                               4912                 :                :                      NULL, NUM_index, NUM_FLAG, &ent->Num);
                               4913                 :                : 
                               4914                 :            401 :         ent->valid = true;
                               4915                 :                :     }
                               4916                 :         699336 :     return ent;
                               4917                 :                : }
                               4918                 :                : 
                               4919                 :                : /*
                               4920                 :                :  * Cache routine for NUM to_char version
                               4921                 :                :  */
                               4922                 :                : static FormatNode *
  302 peter@eisentraut.org     4923                 :         699544 : NUM_cache(int len, NUMDesc *Num, const text *pars_str, bool *shouldFree)
                               4924                 :                : {
 9633 bruce@momjian.us         4925                 :         699544 :     FormatNode *format = NULL;
                               4926                 :                :     char       *str;
                               4927                 :                : 
 6729 tgl@sss.pgh.pa.us        4928                 :         699544 :     str = text_to_cstring(pars_str);
                               4929                 :                : 
 9633 bruce@momjian.us         4930         [ +  + ]:         699544 :     if (len > NUM_CACHE_SIZE)
                               4931                 :                :     {
                               4932                 :                :         /*
                               4933                 :                :          * Allocate new memory if format picture is bigger than static cache
                               4934                 :                :          * and do not use cache (call parser always)
                               4935                 :                :          */
  108 nathan@postgresql.or     4936                 :            200 :         format = palloc_array(FormatNode, len + 1);
                               4937                 :                : 
 8742 bruce@momjian.us         4938                 :            200 :         *shouldFree = true;
                               4939                 :                : 
  301 peter@eisentraut.org     4940                 :            200 :         memset(Num, 0, sizeof *Num);
                               4941                 :                : 
 9633 bruce@momjian.us         4942                 :            200 :         parse_format(format, str, NUM_keywords,
                               4943                 :                :                      NULL, NUM_index, NUM_FLAG, Num);
                               4944                 :                :     }
                               4945                 :                :     else
                               4946                 :                :     {
                               4947                 :                :         /*
                               4948                 :                :          * Use cache buffers
                               4949                 :                :          */
 3620 tgl@sss.pgh.pa.us        4950                 :         699344 :         NUMCacheEntry *ent = NUM_cache_fetch(str);
                               4951                 :                : 
 8742 bruce@momjian.us         4952                 :         699336 :         *shouldFree = false;
                               4953                 :                : 
 9633                          4954                 :         699336 :         format = ent->format;
                               4955                 :                : 
                               4956                 :                :         /*
                               4957                 :                :          * Copy cache to used struct
                               4958                 :                :          */
 4367                          4959                 :         699336 :         Num->flag = ent->Num.flag;
                               4960                 :         699336 :         Num->lsign = ent->Num.lsign;
                               4961                 :         699336 :         Num->pre = ent->Num.pre;
                               4962                 :         699336 :         Num->post = ent->Num.post;
                               4963                 :         699336 :         Num->pre_lsign_num = ent->Num.pre_lsign_num;
                               4964                 :         699336 :         Num->need_locale = ent->Num.need_locale;
                               4965                 :         699336 :         Num->multi = ent->Num.multi;
                               4966                 :         699336 :         Num->zero_start = ent->Num.zero_start;
                               4967                 :         699336 :         Num->zero_end = ent->Num.zero_end;
                               4968                 :                :     }
                               4969                 :                : 
                               4970                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               4971                 :                :     /* dump_node(format, len); */
                               4972                 :                :     dump_index(NUM_keywords, NUM_index);
                               4973                 :                : #endif
                               4974                 :                : 
 9660                          4975                 :         699536 :     pfree(str);
                               4976                 :         699536 :     return format;
                               4977                 :                : }
                               4978                 :                : 
                               4979                 :                : 
                               4980                 :                : /*
                               4981                 :                :  * Convert integer to Roman numerals
                               4982                 :                :  * Result is upper-case and not blank-padded (NUM_processor converts as needed)
                               4983                 :                :  * If input is out-of-range, produce '###############'
                               4984                 :                :  */
                               4985                 :                : static char *
 9711                          4986                 :          16088 : int_to_roman(int number)
                               4987                 :                : {
                               4988                 :                :     int         len,
                               4989                 :                :                 num;
                               4990                 :                :     char       *result,
                               4991                 :                :                 numstr[12];
                               4992                 :                : 
  582 tgl@sss.pgh.pa.us        4993                 :          16088 :     result = (char *) palloc(MAX_ROMAN_LEN + 1);
 9711 bruce@momjian.us         4994                 :          16088 :     *result = '\0';
                               4995                 :                : 
                               4996                 :                :     /*
                               4997                 :                :      * This range limit is the same as in Oracle(TM).  The difficulty with
                               4998                 :                :      * handling 4000 or more is that we'd need to use more than 3 "M"'s, and
                               4999                 :                :      * more than 3 of the same digit isn't considered a valid Roman string.
                               5000                 :                :      */
 9633                          5001   [ +  +  +  + ]:          16088 :     if (number > 3999 || number < 1)
                               5002                 :                :     {
  582 tgl@sss.pgh.pa.us        5003                 :             60 :         fill_str(result, '#', MAX_ROMAN_LEN);
 9711 bruce@momjian.us         5004                 :             60 :         return result;
                               5005                 :                :     }
                               5006                 :                : 
                               5007                 :                :     /* Convert to decimal, then examine each digit */
 9312 ishii@postgresql.org     5008                 :          16028 :     len = snprintf(numstr, sizeof(numstr), "%d", number);
  700 tgl@sss.pgh.pa.us        5009   [ +  -  -  + ]:          16028 :     Assert(len > 0 && len <= 4);
                               5010                 :                : 
  303 peter@eisentraut.org     5011         [ +  + ]:          75688 :     for (char *p = numstr; *p != '\0'; p++, --len)
                               5012                 :                :     {
 2182 tgl@sss.pgh.pa.us        5013                 :          59660 :         num = *p - ('0' + 1);
 9711 bruce@momjian.us         5014         [ +  + ]:          59660 :         if (num < 0)
  700 tgl@sss.pgh.pa.us        5015                 :           4364 :             continue;           /* ignore zeroes */
                               5016                 :                :         /* switch on current column position */
                               5017   [ +  +  +  +  :          55296 :         switch (len)
                                                 - ]
                               5018                 :                :         {
                               5019                 :          12016 :             case 4:
                               5020         [ +  + ]:          36032 :                 while (num-- >= 0)
                               5021                 :          24016 :                     strcat(result, "M");
                               5022                 :          12016 :                 break;
                               5023                 :          14428 :             case 3:
 9633 bruce@momjian.us         5024                 :          14428 :                 strcat(result, rm100[num]);
  700 tgl@sss.pgh.pa.us        5025                 :          14428 :                 break;
                               5026                 :          14424 :             case 2:
 9711 bruce@momjian.us         5027                 :          14424 :                 strcat(result, rm10[num]);
  700 tgl@sss.pgh.pa.us        5028                 :          14424 :                 break;
                               5029                 :          14428 :             case 1:
 9711 bruce@momjian.us         5030                 :          14428 :                 strcat(result, rm1[num]);
  700 tgl@sss.pgh.pa.us        5031                 :          14428 :                 break;
                               5032                 :                :         }
                               5033                 :                :     }
 9711 bruce@momjian.us         5034                 :          16028 :     return result;
                               5035                 :                : }
                               5036                 :                : 
                               5037                 :                : /*
                               5038                 :                :  * Convert a roman numeral (standard form) to an integer.
                               5039                 :                :  * Result is an integer between 1 and 3999.
                               5040                 :                :  * Np->input_p is advanced past the characters consumed.
                               5041                 :                :  *
                               5042                 :                :  * If input is invalid, return -1.
                               5043                 :                :  */
                               5044                 :                : static int
    7 tgl@sss.pgh.pa.us        5045                 :GNC       16072 : roman_to_int(NUMProc *Np)
                               5046                 :                : {
  582 tgl@sss.pgh.pa.us        5047                 :CBC       16072 :     int         result = 0;
                               5048                 :                :     size_t      len;
                               5049                 :                :     char        romanChars[MAX_ROMAN_LEN];
                               5050                 :                :     int         romanValues[MAX_ROMAN_LEN];
                               5051                 :          16072 :     int         repeatCount = 1;
                               5052                 :          16072 :     int         vCount = 0,
                               5053                 :          16072 :                 lCount = 0,
                               5054                 :          16072 :                 dCount = 0;
                               5055                 :          16072 :     bool        subtractionEncountered = false;
                               5056                 :          16072 :     int         lastSubtractedValue = 0;
                               5057                 :                : 
                               5058                 :                :     /*
                               5059                 :                :      * Skip any leading whitespace.  Perhaps we should limit the amount of
                               5060                 :                :      * space skipped to MAX_ROMAN_LEN, but that seems unnecessarily picky.
                               5061                 :                :      */
    7 tgl@sss.pgh.pa.us        5062   [ +  +  +  + ]:GNC      136016 :     while (!OVERLOAD_TEST && isspace((unsigned char) *Np->input_p))
                               5063                 :         119944 :         Np->input_p++;
                               5064                 :                : 
                               5065                 :                :     /*
                               5066                 :                :      * Collect and decode valid roman numerals, consuming at most
                               5067                 :                :      * MAX_ROMAN_LEN characters.  We do this in a separate loop to avoid
                               5068                 :                :      * repeated decoding and because the main loop needs to know when it's at
                               5069                 :                :      * the last numeral.
                               5070                 :                :      */
  582 tgl@sss.pgh.pa.us        5071   [ +  +  +  + ]:CBC      136308 :     for (len = 0; len < MAX_ROMAN_LEN && !OVERLOAD_TEST; len++)
                               5072                 :                :     {
    7 tgl@sss.pgh.pa.us        5073                 :GNC      120252 :         char        currChar = pg_ascii_toupper(*Np->input_p);
  582 tgl@sss.pgh.pa.us        5074   [ +  +  +  +  :CBC      120252 :         int         currValue = ROMAN_VAL(currChar);
                                     +  +  +  +  +  
                                        +  +  +  +  
                                                 + ]
                               5075                 :                : 
                               5076         [ +  + ]:         120252 :         if (currValue == 0)
                               5077                 :             16 :             break;              /* Not a valid roman numeral. */
                               5078                 :         120236 :         romanChars[len] = currChar;
                               5079                 :         120236 :         romanValues[len] = currValue;
    7 tgl@sss.pgh.pa.us        5080                 :GNC      120236 :         Np->input_p++;
                               5081                 :                :     }
                               5082                 :                : 
  582 tgl@sss.pgh.pa.us        5083         [ +  + ]:CBC       16072 :     if (len == 0)
                               5084                 :              8 :         return -1;              /* No valid roman numerals. */
                               5085                 :                : 
                               5086                 :                :     /* Check for valid combinations and compute the represented value. */
  302 peter@eisentraut.org     5087         [ +  + ]:         126600 :     for (size_t i = 0; i < len; i++)
                               5088                 :                :     {
  582 tgl@sss.pgh.pa.us        5089                 :         110584 :         char        currChar = romanChars[i];
                               5090                 :         110584 :         int         currValue = romanValues[i];
                               5091                 :                : 
                               5092                 :                :         /*
                               5093                 :                :          * Ensure no numeral greater than or equal to the subtracted numeral
                               5094                 :                :          * appears after a subtraction.
                               5095                 :                :          */
                               5096   [ +  +  +  + ]:         110584 :         if (subtractionEncountered && currValue >= lastSubtractedValue)
                               5097                 :              4 :             return -1;
                               5098                 :                : 
                               5099                 :                :         /*
                               5100                 :                :          * V, L, and D should not appear before a larger numeral, nor should
                               5101                 :                :          * they be repeated.
                               5102                 :                :          */
                               5103   [ +  +  +  +  :         110580 :         if ((vCount && currValue >= ROMAN_VAL('V')) ||
                                              +  + ]
                               5104   [ +  -  +  + ]:         110576 :             (lCount && currValue >= ROMAN_VAL('L')) ||
                               5105         [ -  + ]:          38420 :             (dCount && currValue >= ROMAN_VAL('D')))
                               5106                 :              4 :             return -1;
                               5107         [ +  + ]:         110576 :         if (currChar == 'V')
                               5108                 :           6416 :             vCount++;
                               5109         [ +  + ]:         104160 :         else if (currChar == 'L')
                               5110                 :           6408 :             lCount++;
                               5111         [ +  + ]:          97752 :         else if (currChar == 'D')
                               5112                 :           6412 :             dCount++;
                               5113                 :                : 
                               5114         [ +  + ]:         110576 :         if (i < len - 1)
                               5115                 :                :         {
                               5116                 :                :             /* Compare current numeral to next numeral. */
                               5117                 :          98120 :             char        nextChar = romanChars[i + 1];
                               5118                 :          98120 :             int         nextValue = romanValues[i + 1];
                               5119                 :                : 
                               5120                 :                :             /*
                               5121                 :                :              * If the current value is less than the next value, handle
                               5122                 :                :              * subtraction. Verify valid subtractive combinations and update
                               5123                 :                :              * the result accordingly.
                               5124                 :                :              */
                               5125         [ +  + ]:          98120 :             if (currValue < nextValue)
                               5126                 :                :             {
                               5127   [ +  +  +  +  :           9648 :                 if (!IS_VALID_SUB_COMB(currChar, nextChar))
                                     +  +  +  +  +  
                                     +  -  +  +  +  
                                        +  +  -  + ]
                               5128                 :              4 :                     return -1;
                               5129                 :                : 
                               5130                 :                :                 /*
                               5131                 :                :                  * Reject cases where same numeral is repeated with
                               5132                 :                :                  * subtraction (e.g. 'MCCM' or 'DCCCD').
                               5133                 :                :                  */
                               5134         [ +  + ]:           9644 :                 if (repeatCount > 1)
                               5135                 :              8 :                     return -1;
                               5136                 :                : 
                               5137                 :                :                 /*
                               5138                 :                :                  * We are going to skip nextChar, so first make checks needed
                               5139                 :                :                  * for V, L, and D.  These are the same as we'd have applied
                               5140                 :                :                  * if we reached nextChar without a subtraction.
                               5141                 :                :                  */
                               5142   [ +  +  -  +  :           9636 :                 if ((vCount && nextValue >= ROMAN_VAL('V')) ||
                                              +  + ]
                               5143   [ +  +  +  + ]:           9628 :                     (lCount && nextValue >= ROMAN_VAL('L')) ||
                               5144         [ +  + ]:           3208 :                     (dCount && nextValue >= ROMAN_VAL('D')))
                               5145                 :             24 :                     return -1;
                               5146         [ +  + ]:           9612 :                 if (nextChar == 'V')
                               5147                 :           1608 :                     vCount++;
                               5148         [ +  + ]:           8004 :                 else if (nextChar == 'L')
                               5149                 :           1600 :                     lCount++;
                               5150         [ +  + ]:           6404 :                 else if (nextChar == 'D')
                               5151                 :           1600 :                     dCount++;
                               5152                 :                : 
                               5153                 :                :                 /*
                               5154                 :                :                  * Skip the next numeral as it is part of the subtractive
                               5155                 :                :                  * combination.
                               5156                 :                :                  */
                               5157                 :           9612 :                 i++;
                               5158                 :                : 
                               5159                 :                :                 /* Update state. */
                               5160                 :           9612 :                 repeatCount = 1;
                               5161                 :           9612 :                 subtractionEncountered = true;
                               5162                 :           9612 :                 lastSubtractedValue = currValue;
                               5163                 :           9612 :                 result += (nextValue - currValue);
                               5164                 :                :             }
                               5165                 :                :             else
                               5166                 :                :             {
                               5167                 :                :                 /* For same numerals, check for repetition. */
                               5168         [ +  + ]:          88472 :                 if (currChar == nextChar)
                               5169                 :                :                 {
                               5170                 :          40852 :                     repeatCount++;
                               5171         [ +  + ]:          40852 :                     if (repeatCount > 3)
                               5172                 :              4 :                         return -1;
                               5173                 :                :                 }
                               5174                 :                :                 else
                               5175                 :          47620 :                     repeatCount = 1;
                               5176                 :          88468 :                 result += currValue;
                               5177                 :                :             }
                               5178                 :                :         }
                               5179                 :                :         else
                               5180                 :                :         {
                               5181                 :                :             /* This is the last numeral; just add it to the result. */
                               5182                 :          12456 :             result += currValue;
                               5183                 :                :         }
                               5184                 :                :     }
                               5185                 :                : 
                               5186                 :          16016 :     return result;
                               5187                 :                : }
                               5188                 :                : 
                               5189                 :                : 
                               5190                 :                : /*
                               5191                 :                :  * Locale
                               5192                 :                :  */
                               5193                 :                : static void
 9711 bruce@momjian.us         5194                 :         699312 : NUM_prepare_locale(NUMProc *Np)
                               5195                 :                : {
 4367                          5196         [ +  + ]:         699312 :     if (Np->Num->need_locale)
                               5197                 :                :     {
                               5198                 :                :         struct lconv *lconv;
                               5199                 :                : 
                               5200                 :                :         /*
                               5201                 :                :          * Get locales
                               5202                 :                :          */
 9711                          5203                 :            558 :         lconv = PGLC_localeconv();
                               5204                 :                : 
                               5205                 :                :         /*
                               5206                 :                :          * Positive / Negative number sign
                               5207                 :                :          */
                               5208   [ +  -  -  + ]:            558 :         if (lconv->negative_sign && *lconv->negative_sign)
 9711 bruce@momjian.us         5209                 :UBC           0 :             Np->L_negative_sign = lconv->negative_sign;
                               5210                 :                :         else
 9711 bruce@momjian.us         5211                 :CBC         558 :             Np->L_negative_sign = "-";
                               5212                 :                : 
 7501                          5213   [ +  -  -  + ]:            558 :         if (lconv->positive_sign && *lconv->positive_sign)
 9711 bruce@momjian.us         5214                 :UBC           0 :             Np->L_positive_sign = lconv->positive_sign;
                               5215                 :                :         else
 9633 bruce@momjian.us         5216                 :CBC         558 :             Np->L_positive_sign = "+";
                               5217                 :                : 
                               5218                 :                :         /*
                               5219                 :                :          * Number decimal point
                               5220                 :                :          */
 9711                          5221   [ +  -  +  - ]:            558 :         if (lconv->decimal_point && *lconv->decimal_point)
                               5222                 :            558 :             Np->decimal = lconv->decimal_point;
                               5223                 :                : 
                               5224                 :                :         else
 9711 bruce@momjian.us         5225                 :UBC           0 :             Np->decimal = ".";
                               5226                 :                : 
 4367 bruce@momjian.us         5227         [ +  + ]:CBC         558 :         if (!IS_LDECIMAL(Np->Num))
 7135                          5228                 :            472 :             Np->decimal = ".";
                               5229                 :                : 
                               5230                 :                :         /*
                               5231                 :                :          * Number thousands separator
                               5232                 :                :          *
                               5233                 :                :          * Some locales (e.g. broken glibc pt_BR), have a comma for decimal,
                               5234                 :                :          * but "" for thousands_sep, so we set the thousands_sep too.
                               5235                 :                :          * http://archives.postgresql.org/pgsql-hackers/2007-11/msg00772.php
                               5236                 :                :          */
                               5237   [ +  -  -  + ]:            558 :         if (lconv->thousands_sep && *lconv->thousands_sep)
 7135 bruce@momjian.us         5238                 :UBC           0 :             Np->L_thousands_sep = lconv->thousands_sep;
                               5239                 :                :         /* Make sure thousands separator doesn't match decimal point symbol. */
 2294 tgl@sss.pgh.pa.us        5240         [ +  - ]:CBC         558 :         else if (strcmp(Np->decimal, ",") != 0)
 7135 bruce@momjian.us         5241                 :            558 :             Np->L_thousands_sep = ",";
                               5242                 :                :         else
 6854 bruce@momjian.us         5243                 :UBC           0 :             Np->L_thousands_sep = ".";
                               5244                 :                : 
                               5245                 :                :         /*
                               5246                 :                :          * Currency symbol
                               5247                 :                :          */
 9633 bruce@momjian.us         5248   [ +  -  -  + ]:CBC         558 :         if (lconv->currency_symbol && *lconv->currency_symbol)
 9711 bruce@momjian.us         5249                 :UBC           0 :             Np->L_currency_symbol = lconv->currency_symbol;
                               5250                 :                :         else
 9633 bruce@momjian.us         5251                 :CBC         558 :             Np->L_currency_symbol = " ";
                               5252                 :                :     }
                               5253                 :                :     else
                               5254                 :                :     {
                               5255                 :                :         /*
                               5256                 :                :          * Default values
                               5257                 :                :          */
                               5258                 :         698754 :         Np->L_negative_sign = "-";
                               5259                 :         698754 :         Np->L_positive_sign = "+";
                               5260                 :         698754 :         Np->decimal = ".";
                               5261                 :                : 
                               5262                 :         698754 :         Np->L_thousands_sep = ",";
                               5263                 :         698754 :         Np->L_currency_symbol = " ";
                               5264                 :                :     }
 9711                          5265                 :         699312 : }
                               5266                 :                : 
                               5267                 :                : /*
                               5268                 :                :  * Return pointer of last relevant number after decimal point
                               5269                 :                :  *  12.0500 --> last relevant is '5'
                               5270                 :                :  *  12.0000 --> last relevant is '.'
                               5271                 :                :  * If there is no decimal point, return NULL (which will result in same
                               5272                 :                :  * behavior as if FM hadn't been specified).
                               5273                 :                :  */
                               5274                 :                : static const char *
  302 peter@eisentraut.org     5275                 :            452 : get_last_relevant_decnum(const char *num)
                               5276                 :                : {
                               5277                 :                :     const char *result,
 9289 bruce@momjian.us         5278                 :            452 :                *p = strchr(num, '.');
                               5279                 :                : 
                               5280                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5281                 :                :     elog(DEBUG_elog_output, "get_last_relevant_decnum()");
                               5282                 :                : #endif
                               5283                 :                : 
 9633                          5284         [ +  + ]:            452 :     if (!p)
 5468 tgl@sss.pgh.pa.us        5285                 :              4 :         return NULL;
                               5286                 :                : 
 9697 bruce@momjian.us         5287                 :            448 :     result = p;
                               5288                 :                : 
 9633                          5289         [ +  + ]:           6628 :     while (*(++p))
                               5290                 :                :     {
                               5291         [ +  + ]:           6180 :         if (*p != '0')
 9697                          5292                 :           1296 :             result = p;
                               5293                 :                :     }
                               5294                 :                : 
                               5295                 :            448 :     return result;
                               5296                 :                : }
                               5297                 :                : 
                               5298                 :                : 
                               5299                 :                : /*
                               5300                 :                :  * Macros used by both TO_NUMBER() and TO_CHAR() code
                               5301                 :                :  */
                               5302                 :                : #define NUM_EMITF(...) appendStringInfo(Np->output, __VA_ARGS__)
                               5303                 :                : #define NUM_EMITS(str) appendStringInfoString(Np->output, str)
                               5304                 :                : #define NUM_EMITC(chr) appendStringInfoChar(Np->output, chr)
                               5305                 :                : 
                               5306                 :                : /*
                               5307                 :                :  * Number extraction for TO_NUMBER()
                               5308                 :                :  */
                               5309                 :                : static void
    7 tgl@sss.pgh.pa.us        5310                 :GNC         582 : NUM_numpart_from_char(NUMProc *Np, int id)
                               5311                 :                : {
 3298 peter_e@gmx.net          5312                 :CBC         582 :     bool        isread = false;
                               5313                 :                : 
                               5314                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5315                 :                :     elog(DEBUG_elog_output, " --- scan start --- id=%s",
                               5316                 :                :          (id == NUM_0 || id == NUM_9) ? "NUM_0/9" : id == NUM_DEC ? "NUM_DEC" : "???");
                               5317                 :                : #endif
                               5318                 :                : 
 3671                          5319         [ -  + ]:            582 :     if (OVERLOAD_TEST)
 3671 peter_e@gmx.net          5320                 :UBC           0 :         return;
                               5321                 :                : 
    7 tgl@sss.pgh.pa.us        5322         [ -  + ]:GNC         582 :     if (*Np->input_p == ' ')
    7 tgl@sss.pgh.pa.us        5323                 :UNC           0 :         Np->input_p++;
                               5324                 :                : 
 9668 bruce@momjian.us         5325         [ -  + ]:CBC         582 :     if (OVERLOAD_TEST)
 9668 bruce@momjian.us         5326                 :UBC           0 :         return;
                               5327                 :                : 
                               5328                 :                :     /*
                               5329                 :                :      * read sign before number
                               5330                 :                :      */
    7 tgl@sss.pgh.pa.us        5331         [ -  + ]:GNC         582 :     Assert(Np->output->len > 0);
                               5332   [ +  +  +  -  :            582 :     if (Np->output->data[0] == ' ' && (id == NUM_0 || id == NUM_9) &&
                                              +  + ]
 7621 bruce@momjian.us         5333         [ +  + ]:CBC         392 :         (Np->read_pre + Np->read_post) == 0)
                               5334                 :                :     {
                               5335                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5336                 :                :         elog(DEBUG_elog_output, "Try read sign (%c), locale positive: %s, negative: %s",
                               5337                 :                :              *Np->input_p, Np->L_positive_sign, Np->L_negative_sign);
                               5338                 :                : #endif
                               5339                 :                : 
                               5340                 :                :         /*
                               5341                 :                :          * locale sign
                               5342                 :                :          */
 4367                          5343   [ +  +  +  + ]:            114 :         if (IS_LSIGN(Np->Num) && Np->Num->lsign == NUM_LSIGN_PRE)
 9633                          5344                 :              8 :         {
  302 peter@eisentraut.org     5345                 :              8 :             size_t      x = 0;
                               5346                 :                : 
                               5347                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5348                 :                :             elog(DEBUG_elog_output, "Try read locale pre-sign (%c)", *Np->input_p);
                               5349                 :                : #endif
 7621 bruce@momjian.us         5350         [ +  - ]:              8 :             if ((x = strlen(Np->L_negative_sign)) &&
 7973 tgl@sss.pgh.pa.us        5351         [ +  - ]:              8 :                 AMOUNT_TEST(x) &&
    7 tgl@sss.pgh.pa.us        5352         [ +  + ]:GNC           8 :                 strncmp(Np->input_p, Np->L_negative_sign, x) == 0)
                               5353                 :                :             {
                               5354                 :              4 :                 Np->input_p += x;
                               5355                 :              4 :                 Np->output->data[0] = '-';
                               5356                 :                :             }
 7621 bruce@momjian.us         5357         [ +  - ]:CBC           4 :             else if ((x = strlen(Np->L_positive_sign)) &&
                               5358         [ +  - ]:              4 :                      AMOUNT_TEST(x) &&
    7 tgl@sss.pgh.pa.us        5359         [ -  + ]:GNC           4 :                      strncmp(Np->input_p, Np->L_positive_sign, x) == 0)
                               5360                 :                :             {
    7 tgl@sss.pgh.pa.us        5361                 :UNC           0 :                 Np->input_p += x;
                               5362                 :              0 :                 Np->output->data[0] = '+';
                               5363                 :                :             }
                               5364                 :                :         }
                               5365                 :                :         else
                               5366                 :                :         {
                               5367                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5368                 :                :             elog(DEBUG_elog_output, "Try read simple sign (%c)", *Np->input_p);
                               5369                 :                : #endif
                               5370                 :                : 
                               5371                 :                :             /*
                               5372                 :                :              * simple + - < >
                               5373                 :                :              */
    7 tgl@sss.pgh.pa.us        5374   [ +  +  +  + ]:GNC         106 :             if (*Np->input_p == '-' || (IS_BRACKET(Np->Num) &&
                               5375         [ +  - ]:              4 :                                         *Np->input_p == '<'))
                               5376                 :                :             {
                               5377                 :             12 :                 Np->output->data[0] = '-';    /* set - */
                               5378                 :             12 :                 Np->input_p++;
                               5379                 :                :             }
                               5380         [ -  + ]:             94 :             else if (*Np->input_p == '+')
                               5381                 :                :             {
    7 tgl@sss.pgh.pa.us        5382                 :UNC           0 :                 Np->output->data[0] = '+';    /* set + */
                               5383                 :              0 :                 Np->input_p++;
                               5384                 :                :             }
                               5385                 :                :         }
                               5386                 :                :     }
                               5387                 :                : 
 9668 bruce@momjian.us         5388         [ -  + ]:CBC         582 :     if (OVERLOAD_TEST)
 9668 bruce@momjian.us         5389                 :UBC           0 :         return;
                               5390                 :                : 
                               5391                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5392                 :                :     elog(DEBUG_elog_output, "Scan for numbers (%c), current output: '%s'", *Np->input_p, Np->output->data);
                               5393                 :                : #endif
                               5394                 :                : 
                               5395                 :                :     /*
                               5396                 :                :      * read digit or decimal point
                               5397                 :                :      */
    7 tgl@sss.pgh.pa.us        5398         [ +  + ]:GNC         582 :     if (isdigit((unsigned char) *Np->input_p))
                               5399                 :                :     {
 4367 bruce@momjian.us         5400   [ +  +  -  + ]:CBC         496 :         if (Np->read_dec && Np->read_post == Np->Num->post)
 9697 bruce@momjian.us         5401                 :UBC           0 :             return;
                               5402                 :                : 
    7 tgl@sss.pgh.pa.us        5403                 :GNC         496 :         NUM_EMITC(*Np->input_p);
                               5404                 :                : 
 9697 bruce@momjian.us         5405         [ +  + ]:CBC         496 :         if (Np->read_dec)
                               5406                 :            174 :             Np->read_post++;
                               5407                 :                :         else
 7973 tgl@sss.pgh.pa.us        5408                 :            322 :             Np->read_pre++;
                               5409                 :                : 
 3298 peter_e@gmx.net          5410                 :            496 :         isread = true;
                               5411                 :                : 
                               5412                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5413                 :                :         elog(DEBUG_elog_output, "Read digit (%c)", *Np->input_p);
                               5414                 :                : #endif
                               5415                 :                :     }
                               5416   [ +  +  +  + ]:             86 :     else if (IS_DECIMAL(Np->Num) && Np->read_dec == false)
                               5417                 :                :     {
                               5418                 :                :         /*
                               5419                 :                :          * We need not test IS_LDECIMAL(Np->Num) explicitly here, because
                               5420                 :                :          * Np->decimal is always just "." if we don't have a D format token.
                               5421                 :                :          * So we just unconditionally match to Np->decimal.
                               5422                 :                :          */
  302 peter@eisentraut.org     5423                 :             78 :         size_t      x = strlen(Np->decimal);
                               5424                 :                : 
                               5425                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5426                 :                :         elog(DEBUG_elog_output, "Try read decimal point (%c)",
                               5427                 :                :              *Np->input_p);
                               5428                 :                : #endif
    7 tgl@sss.pgh.pa.us        5429   [ +  -  +  -  :GNC          78 :         if (x && AMOUNT_TEST(x) && strncmp(Np->input_p, Np->decimal, x) == 0)
                                              +  + ]
                               5430                 :                :         {
                               5431                 :             70 :             Np->input_p += x - 1;
                               5432                 :             70 :             NUM_EMITC('.');
 3298 peter_e@gmx.net          5433                 :CBC          70 :             Np->read_dec = true;
                               5434                 :             70 :             isread = true;
                               5435                 :                :         }
                               5436                 :                :     }
                               5437                 :                : 
 7973 tgl@sss.pgh.pa.us        5438         [ -  + ]:            582 :     if (OVERLOAD_TEST)
 7973 tgl@sss.pgh.pa.us        5439                 :UBC           0 :         return;
                               5440                 :                : 
                               5441                 :                :     /*
                               5442                 :                :      * Read sign behind "last" number
                               5443                 :                :      *
                               5444                 :                :      * We need sign detection because determine exact position of post-sign is
                               5445                 :                :      * difficult:
                               5446                 :                :      *
                               5447                 :                :      * FM9999.9999999S     -> 123.001- 9.9S             -> .5- FM9.999999MI ->
                               5448                 :                :      * 5.01-
                               5449                 :                :      */
    7 tgl@sss.pgh.pa.us        5450   [ +  +  +  + ]:GNC         582 :     if (Np->output->data[0] == ' ' && Np->read_pre + Np->read_post > 0)
                               5451                 :                :     {
                               5452                 :                :         /*
                               5453                 :                :          * locale sign (NUM_S) is always anchored behind a last number, if: -
                               5454                 :                :          * locale sign expected - last read char was NUM_0/9 or NUM_DEC - and
                               5455                 :                :          * next char is not digit
                               5456                 :                :          */
 4367 bruce@momjian.us         5457   [ +  +  +  - ]:CBC         410 :         if (IS_LSIGN(Np->Num) && isread &&
    7 tgl@sss.pgh.pa.us        5458         [ +  - ]:GNC         102 :             (Np->input_p + 1) < Np->input_end &&
                               5459         [ +  + ]:            102 :             !isdigit((unsigned char) *(Np->input_p + 1)))
 7973 tgl@sss.pgh.pa.us        5460                 :CBC          46 :         {
                               5461                 :                :             size_t      x;
    7 tgl@sss.pgh.pa.us        5462                 :GNC          46 :             const char *tmp = Np->input_p++;
                               5463                 :                : 
                               5464                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5465                 :                :             elog(DEBUG_elog_output, "Try read locale post-sign (%c)", *Np->input_p);
                               5466                 :                : #endif
 7621 bruce@momjian.us         5467         [ +  - ]:CBC          46 :             if ((x = strlen(Np->L_negative_sign)) &&
 7973 tgl@sss.pgh.pa.us        5468         [ +  - ]:             46 :                 AMOUNT_TEST(x) &&
    7 tgl@sss.pgh.pa.us        5469         [ +  + ]:GNC          46 :                 strncmp(Np->input_p, Np->L_negative_sign, x) == 0)
                               5470                 :                :             {
                               5471                 :             22 :                 Np->input_p += x - 1;
                               5472                 :                :                 /* NUM_processor_from_char() will do input_p++ */
                               5473                 :             22 :                 Np->output->data[0] = '-';
                               5474                 :                :             }
 7621 bruce@momjian.us         5475         [ +  - ]:CBC          24 :             else if ((x = strlen(Np->L_positive_sign)) &&
                               5476         [ +  - ]:             24 :                      AMOUNT_TEST(x) &&
    7 tgl@sss.pgh.pa.us        5477         [ -  + ]:GNC          24 :                      strncmp(Np->input_p, Np->L_positive_sign, x) == 0)
                               5478                 :                :             {
    7 tgl@sss.pgh.pa.us        5479                 :UNC           0 :                 Np->input_p += x - 1;
                               5480                 :                :                 /* NUM_processor_from_char() will do input_p++ */
                               5481                 :              0 :                 Np->output->data[0] = '+';
                               5482                 :                :             }
    7 tgl@sss.pgh.pa.us        5483         [ +  + ]:GNC          46 :             if (Np->output->data[0] == ' ')
                               5484                 :                :                 /* no sign read */
                               5485                 :             24 :                 Np->input_p = tmp;
                               5486                 :                :         }
                               5487                 :                : 
                               5488                 :                :         /*
                               5489                 :                :          * try read non-locale sign, which happens only if format is not exact
                               5490                 :                :          * and we cannot determine sign position of MI/PL/SG, an example:
                               5491                 :                :          *
                               5492                 :                :          * FM9.999999MI            -> 5.01-
                               5493                 :                :          *
                               5494                 :                :          * if (.... && IS_LSIGN(Np->Num)==false) prevents read wrong formats
                               5495                 :                :          * like to_number('1 -', '9S') where sign is not anchored to last
                               5496                 :                :          * number.
                               5497                 :                :          */
 3298 peter_e@gmx.net          5498   [ +  +  +  - ]:CBC         364 :         else if (isread == false && IS_LSIGN(Np->Num) == false &&
 4367 bruce@momjian.us         5499   [ +  -  +  + ]:             16 :                  (IS_PLUS(Np->Num) || IS_MINUS(Np->Num)))
                               5500                 :                :         {
                               5501                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5502                 :                :             elog(DEBUG_elog_output, "Try read simple post-sign (%c)", *Np->input_p);
                               5503                 :                : #endif
                               5504                 :                : 
                               5505                 :                :             /*
                               5506                 :                :              * simple + -
                               5507                 :                :              */
    7 tgl@sss.pgh.pa.us        5508   [ -  +  -  - ]:GNC           4 :             if (*Np->input_p == '-' || *Np->input_p == '+')
                               5509                 :                :             {
                               5510                 :              4 :                 Np->output->data[0] = *Np->input_p;
                               5511                 :                :                 /* NUM_processor_from_char() will do input_p++ */
                               5512                 :                :             }
                               5513                 :                :         }
                               5514                 :                :     }
                               5515                 :                : }
                               5516                 :                : 
                               5517                 :                : #define IS_PREDEC_SPACE(_n) \
                               5518                 :                :         (IS_ZERO((_n)->Num)==false && \
                               5519                 :                :          (_n)->input == (_n)->input_p && \
                               5520                 :                :          *(_n)->input == '0' && \
                               5521                 :                :                  (_n)->Num->post != 0)
                               5522                 :                : 
                               5523                 :                : /*
                               5524                 :                :  * Add digit or sign to number-string
                               5525                 :                :  */
                               5526                 :                : static void
 9633 bruce@momjian.us         5527                 :CBC     5898865 : NUM_numpart_to_char(NUMProc *Np, int id)
                               5528                 :                : {
                               5529                 :                :     int         end;
                               5530                 :                : 
 4367                          5531         [ -  + ]:        5898865 :     if (IS_ROMAN(Np->Num))
 9697 bruce@momjian.us         5532                 :UBC           0 :         return;
                               5533                 :                : 
                               5534                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5535                 :                : 
                               5536                 :                :     /*
                               5537                 :                :      * Np->num_curr is number of current item in format-picture, it is not
                               5538                 :                :      * current position in output!
                               5539                 :                :      */
                               5540                 :                :     elog(DEBUG_elog_output,
                               5541                 :                :          "SIGN_WROTE: %d, CURRENT: %d, INPUT_P: \"%s\", OUTPUT: \"%s\"",
                               5542                 :                :          Np->sign_wrote,
                               5543                 :                :          Np->num_curr,
                               5544                 :                :          Np->input_p,
                               5545                 :                :          Np->output->data);
                               5546                 :                : #endif
 3298 peter_e@gmx.net          5547                 :CBC     5898865 :     Np->num_in = false;
                               5548                 :                : 
                               5549                 :                :     /*
                               5550                 :                :      * Write sign if real number will write to output Note: IS_PREDEC_SPACE()
                               5551                 :                :      * handle "9.9" --> " .1"
                               5552                 :                :      */
                               5553         [ +  + ]:        5898865 :     if (Np->sign_wrote == false &&
 4367 bruce@momjian.us         5554   [ +  +  +  +  :           9966 :         (Np->num_curr >= Np->out_pre_spaces || (IS_ZERO(Np->Num) && Np->Num->zero_start == Np->num_curr)) &&
                                        +  +  +  + ]
 3298 peter_e@gmx.net          5555   [ +  +  +  +  :           2027 :         (IS_PREDEC_SPACE(Np) == false || (Np->last_relevant && *Np->last_relevant == '.')))
                                     +  +  +  +  +  
                                           +  +  - ]
                               5556                 :                :     {
 4367 bruce@momjian.us         5557         [ +  + ]:           1931 :         if (IS_LSIGN(Np->Num))
                               5558                 :                :         {
                               5559         [ +  + ]:           1290 :             if (Np->Num->lsign == NUM_LSIGN_PRE)
                               5560                 :                :             {
    7 tgl@sss.pgh.pa.us        5561         [ +  + ]:GNC         240 :                 NUM_EMITS((Np->sign == '-') ?
                               5562                 :                :                           Np->L_negative_sign :
                               5563                 :                :                           Np->L_positive_sign);
 3298 peter_e@gmx.net          5564                 :CBC         240 :                 Np->sign_wrote = true;
                               5565                 :                :             }
                               5566                 :                :         }
 4367 bruce@momjian.us         5567         [ +  + ]:            641 :         else if (IS_BRACKET(Np->Num))
                               5568                 :                :         {
    7 tgl@sss.pgh.pa.us        5569         [ +  + ]:GNC          96 :             NUM_EMITC(Np->sign == '+' ? ' ' : '<');
 3298 peter_e@gmx.net          5570                 :CBC          96 :             Np->sign_wrote = true;
                               5571                 :                :         }
 9633 bruce@momjian.us         5572         [ +  + ]:            545 :         else if (Np->sign == '+')
                               5573                 :                :         {
 4367                          5574         [ +  - ]:            365 :             if (!IS_FILLMODE(Np->Num))
                               5575                 :                :             {
    7 tgl@sss.pgh.pa.us        5576                 :GNC         365 :                 NUM_EMITC(' '); /* Write + */
                               5577                 :                :             }
 3298 peter_e@gmx.net          5578                 :CBC         365 :             Np->sign_wrote = true;
                               5579                 :                :         }
 9633 bruce@momjian.us         5580         [ +  - ]:            180 :         else if (Np->sign == '-')
                               5581                 :                :         {                       /* Write - */
    7 tgl@sss.pgh.pa.us        5582                 :GNC         180 :             NUM_EMITC('-');
 3298 peter_e@gmx.net          5583                 :CBC         180 :             Np->sign_wrote = true;
                               5584                 :                :         }
                               5585                 :                :     }
                               5586                 :                : 
                               5587                 :                : 
                               5588                 :                :     /*
                               5589                 :                :      * digits / FM / Zero / Dec. point
                               5590                 :                :      */
 8554 bruce@momjian.us         5591   [ +  +  +  +  :        5898865 :     if (id == NUM_9 || id == NUM_0 || id == NUM_D || id == NUM_DEC)
                                        +  +  +  - ]
                               5592                 :                :     {
 4374                          5593         [ +  + ]:        5898865 :         if (Np->num_curr < Np->out_pre_spaces &&
 4367                          5594   [ +  +  +  + ]:        3567839 :             (Np->Num->zero_start > Np->num_curr || !IS_ZERO(Np->Num)))
                               5595                 :                :         {
                               5596                 :                :             /*
                               5597                 :                :              * Write blank space
                               5598                 :                :              */
                               5599         [ +  + ]:          10428 :             if (!IS_FILLMODE(Np->Num))
                               5600                 :                :             {
    7 tgl@sss.pgh.pa.us        5601                 :GNC        6592 :                 NUM_EMITC(' '); /* Write ' ' */
                               5602                 :                :             }
                               5603                 :                :         }
 4367 bruce@momjian.us         5604         [ +  + ]:CBC     5888437 :         else if (IS_ZERO(Np->Num) &&
 4374                          5605         [ +  + ]:        5870462 :                  Np->num_curr < Np->out_pre_spaces &&
 4367                          5606         [ +  - ]:        3557411 :                  Np->Num->zero_start <= Np->num_curr)
                               5607                 :                :         {
                               5608                 :                :             /*
                               5609                 :                :              * Write ZERO
                               5610                 :                :              */
    7 tgl@sss.pgh.pa.us        5611                 :GNC     3557411 :             NUM_EMITC('0');     /* Write '0' */
 3298 peter_e@gmx.net          5612                 :CBC     3557411 :             Np->num_in = true;
                               5613                 :                :         }
                               5614                 :                :         else
                               5615                 :                :         {
                               5616                 :                :             /*
                               5617                 :                :              * Write Decimal point
                               5618                 :                :              */
    7 tgl@sss.pgh.pa.us        5619         [ +  + ]:GNC     2331026 :             if (*Np->input_p == '.')
                               5620                 :                :             {
 9633 bruce@momjian.us         5621   [ +  +  +  + ]:CBC        1044 :                 if (!Np->last_relevant || *Np->last_relevant != '.')
                               5622                 :                :                 {
    7 tgl@sss.pgh.pa.us        5623                 :GNC         924 :                     NUM_EMITS(Np->decimal); /* Write DEC/D */
                               5624                 :                :                 }
                               5625                 :                : 
                               5626                 :                :                 /*
                               5627                 :                :                  * Ora 'n' -- FM9.9 --> 'n.'
                               5628                 :                :                  */
 4367 bruce@momjian.us         5629         [ +  - ]:CBC         120 :                 else if (IS_FILLMODE(Np->Num) &&
 9633                          5630   [ +  -  +  - ]:            120 :                          Np->last_relevant && *Np->last_relevant == '.')
                               5631                 :                :                 {
    7 tgl@sss.pgh.pa.us        5632                 :GNC         120 :                     NUM_EMITS(Np->decimal); /* Write DEC/D */
                               5633                 :                :                 }
                               5634                 :                :             }
                               5635                 :                :             else
                               5636                 :                :             {
                               5637                 :                :                 /*
                               5638                 :                :                  * Write Digits
                               5639                 :                :                  */
                               5640   [ +  +  +  +  :        2329982 :                 if (Np->last_relevant && Np->input_p > Np->last_relevant &&
                                              +  + ]
                               5641                 :                :                     id != NUM_0)
                               5642                 :                :                     ;
                               5643                 :                : 
                               5644                 :                :                 /*
                               5645                 :                :                  * '0.1' -- 9.9 --> '  .1'
                               5646                 :                :                  */
 8554 bruce@momjian.us         5647   [ +  +  +  +  :CBC     2325534 :                 else if (IS_PREDEC_SPACE(Np))
                                        +  +  +  + ]
                               5648                 :                :                 {
 4367                          5649         [ +  + ]:            152 :                     if (!IS_FILLMODE(Np->Num))
                               5650                 :                :                     {
    7 tgl@sss.pgh.pa.us        5651                 :GNC         104 :                         NUM_EMITC(' ');
                               5652                 :                :                     }
                               5653                 :                : 
                               5654                 :                :                     /*
                               5655                 :                :                      * '0' -- FM9.9 --> '0.'
                               5656                 :                :                      */
 9633 bruce@momjian.us         5657   [ +  -  +  + ]:CBC          48 :                     else if (Np->last_relevant && *Np->last_relevant == '.')
                               5658                 :                :                     {
    7 tgl@sss.pgh.pa.us        5659                 :GNC          40 :                         NUM_EMITC('0');
                               5660                 :                :                     }
                               5661                 :                :                 }
                               5662                 :                :                 else
                               5663                 :                :                 {
                               5664                 :        2325382 :                     NUM_EMITC(*Np->input_p); /* Write DIGIT */
 3298 peter_e@gmx.net          5665                 :CBC     2325382 :                     Np->num_in = true;
                               5666                 :                :                 }
                               5667                 :                :             }
                               5668                 :                :             /* do no exceed string length */
    7 tgl@sss.pgh.pa.us        5669         [ +  + ]:GNC     2331026 :             if (*Np->input_p)
                               5670                 :        2330798 :                 ++Np->input_p;
                               5671                 :                :         }
                               5672                 :                : 
 4367 bruce@momjian.us         5673                 :CBC     5898865 :         end = Np->num_count + (Np->out_pre_spaces ? 1 : 0) + (IS_DECIMAL(Np->Num) ? 1 : 0);
                               5674                 :                : 
    7 tgl@sss.pgh.pa.us        5675   [ +  +  +  + ]:GNC     5898865 :         if (Np->last_relevant && Np->last_relevant == Np->input_p)
 8554 bruce@momjian.us         5676                 :CBC         448 :             end = Np->num_curr;
                               5677                 :                : 
 8424                          5678         [ +  + ]:        5898865 :         if (Np->num_curr + 1 == end)
                               5679                 :                :         {
 3298 peter_e@gmx.net          5680   [ +  +  +  + ]:         667034 :             if (Np->sign_wrote == true && IS_BRACKET(Np->Num))
                               5681                 :                :             {
    7 tgl@sss.pgh.pa.us        5682         [ +  + ]:GNC          96 :                 NUM_EMITC(Np->sign == '+' ? ' ' : '>');
                               5683                 :                :             }
 4367 bruce@momjian.us         5684   [ +  +  +  + ]:CBC      666938 :             else if (IS_LSIGN(Np->Num) && Np->Num->lsign == NUM_LSIGN_POST)
                               5685                 :                :             {
    7 tgl@sss.pgh.pa.us        5686         [ +  + ]:GNC          61 :                 NUM_EMITS((Np->sign == '-') ?
                               5687                 :                :                           Np->L_negative_sign :
                               5688                 :                :                           Np->L_positive_sign);
                               5689                 :                :             }
                               5690                 :                :         }
                               5691                 :                :     }
                               5692                 :                : 
 9697 bruce@momjian.us         5693                 :CBC     5898865 :     ++Np->num_curr;
                               5694                 :                : }
                               5695                 :                : 
                               5696                 :                : /*
                               5697                 :                :  * Skip over "n" input characters, but only if they aren't numeric data
                               5698                 :                :  */
                               5699                 :                : static void
    7 tgl@sss.pgh.pa.us        5700                 :GNC          24 : NUM_eat_non_data_chars(NUMProc *Np, int n)
                               5701                 :                : {
                               5702                 :             24 :     const char *end = Np->input_end;
                               5703                 :                : 
 3205 tgl@sss.pgh.pa.us        5704         [ +  + ]:CBC          44 :     while (n-- > 0)
                               5705                 :                :     {
                               5706         [ -  + ]:             28 :         if (OVERLOAD_TEST)
 3205 tgl@sss.pgh.pa.us        5707                 :UBC           0 :             break;              /* end of input */
    7 tgl@sss.pgh.pa.us        5708         [ +  + ]:GNC          28 :         if (strchr("0123456789.,+-", *Np->input_p) != NULL)
 3205 tgl@sss.pgh.pa.us        5709                 :CBC           8 :             break;              /* it's a data character */
    7 tgl@sss.pgh.pa.us        5710                 :GNC          20 :         Np->input_p += pg_mblen_range(Np->input_p, end);
                               5711                 :                :     }
 3205 tgl@sss.pgh.pa.us        5712                 :CBC          24 : }
                               5713                 :                : 
                               5714                 :                : /*
                               5715                 :                :  * Numeric format processing for TO_NUMBER.
                               5716                 :                :  *
                               5717                 :                :  * We parse the string in "input" according to the format, and build a
                               5718                 :                :  * standard-format representation of the number in "output" (which will
                               5719                 :                :  * be fed to numeric_in()).
                               5720                 :                :  *
                               5721                 :                :  * node: array of FormatNodes representing the parsed format string
                               5722                 :                :  * Num: input/output argument holding additional format flags and state
                               5723                 :                :  * input: input string (not null-terminated!)
                               5724                 :                :  * input_len: length of input string
                               5725                 :                :  * output: output buffer (must be empty initially!)
                               5726                 :                :  * collid: active collation
                               5727                 :                :  */
                               5728                 :                : static void
    7 tgl@sss.pgh.pa.us        5729                 :GNC       16174 : NUM_processor_from_char(const FormatNode *node, NUMDesc *Num,
                               5730                 :                :                         const char *input, size_t input_len,
                               5731                 :                :                         StringInfo output,
                               5732                 :                :                         Oid collid)
                               5733                 :                : {
                               5734                 :                :     NUMProc     _Np,
 9289 bruce@momjian.us         5735                 :CBC       16174 :                *Np = &_Np;
                               5736                 :                :     const char *pattern;
                               5737                 :                :     size_t      pattern_len;
                               5738                 :                : 
 8548 tgl@sss.pgh.pa.us        5739   [ +  -  +  -  :         274958 :     MemSet(Np, 0, sizeof(NUMProc));
                                     +  -  +  -  +  
                                                 + ]
                               5740                 :                : 
 4367 bruce@momjian.us         5741                 :          16174 :     Np->Num = Num;
    7 tgl@sss.pgh.pa.us        5742                 :GNC       16174 :     Np->input = input;
                               5743                 :          16174 :     Np->input_end = input + input_len;
                               5744                 :          16174 :     Np->output = output;
 9697 bruce@momjian.us         5745                 :CBC       16174 :     Np->last_relevant = NULL;
 9633                          5746                 :          16174 :     Np->read_post = 0;
 7621                          5747                 :          16174 :     Np->read_pre = 0;
 3298 peter_e@gmx.net          5748                 :          16174 :     Np->read_dec = false;
                               5749                 :                : 
 4367 bruce@momjian.us         5750         [ -  + ]:          16174 :     if (Np->Num->zero_start)
 4367 bruce@momjian.us         5751                 :LBC    (665752) :         --Np->Num->zero_start;
                               5752                 :                : 
 4367 bruce@momjian.us         5753         [ -  + ]:CBC       16174 :     if (IS_EEEE(Np->Num))
    7 tgl@sss.pgh.pa.us        5754         [ #  # ]:UNC           0 :         ereport(ERROR,
                               5755                 :                :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                               5756                 :                :                  errmsg("\"EEEE\" not supported for input")));
                               5757                 :                : 
                               5758                 :                :     /*
                               5759                 :                :      * Sign
                               5760                 :                :      */
    7 tgl@sss.pgh.pa.us        5761                 :GNC       16174 :     Np->sign = false;
                               5762                 :                : 
                               5763                 :                :     /*
                               5764                 :                :      * Count
                               5765                 :                :      */
 4367 bruce@momjian.us         5766                 :CBC       16174 :     Np->num_count = Np->Num->post + Np->Num->pre - 1;
                               5767                 :                : 
                               5768                 :                :     /*
                               5769                 :                :      * Initialize first character of output buffer with a space.  Later, we
                               5770                 :                :      * may overwrite that with '+' or '-'.
                               5771                 :                :      */
    7 tgl@sss.pgh.pa.us        5772         [ -  + ]:GNC       16174 :     Assert(Np->output->len == 0);
                               5773                 :          16174 :     NUM_EMITC(' ');
                               5774                 :                : 
 9633 bruce@momjian.us         5775                 :CBC       16174 :     Np->num_in = 0;
                               5776                 :          16174 :     Np->num_curr = 0;
                               5777                 :                : 
                               5778                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5779                 :                :     elog(DEBUG_elog_output,
                               5780                 :                :          "\n\tSIGN: '%c'\n\tNUM: '%s'\n\tPRE: %d\n\tPOST: %d\n\tNUM_COUNT: %d\n\tNUM_PRE: %d\n\tSIGN_WROTE: %s\n\tZERO: %s\n\tZERO_START: %d\n\tZERO_END: %d\n\tLAST_RELEVANT: %s\n\tBRACKET: %s\n\tPLUS: %s\n\tMINUS: %s\n\tFILLMODE: %s\n\tROMAN: %s\n\tEEEE: %s",
                               5781                 :                :          Np->sign,
                               5782                 :                :          Np->output->data,
                               5783                 :                :          Np->Num->pre,
                               5784                 :                :          Np->Num->post,
                               5785                 :                :          Np->num_count,
                               5786                 :                :          Np->out_pre_spaces,
                               5787                 :                :          Np->sign_wrote ? "Yes" : "No",
                               5788                 :                :          IS_ZERO(Np->Num) ? "Yes" : "No",
                               5789                 :                :          Np->Num->zero_start,
                               5790                 :                :          Np->Num->zero_end,
                               5791                 :                :          Np->last_relevant ? Np->last_relevant : "<not set>",
                               5792                 :                :          IS_BRACKET(Np->Num) ? "Yes" : "No",
                               5793                 :                :          IS_PLUS(Np->Num) ? "Yes" : "No",
                               5794                 :                :          IS_MINUS(Np->Num) ? "Yes" : "No",
                               5795                 :                :          IS_FILLMODE(Np->Num) ? "Yes" : "No",
                               5796                 :                :          IS_ROMAN(Np->Num) ? "Yes" : "No",
                               5797                 :                :          IS_EEEE(Np->Num) ? "Yes" : "No"
                               5798                 :                :         );
                               5799                 :                : #endif
                               5800                 :                : 
                               5801                 :                :     /*
                               5802                 :                :      * Locale
                               5803                 :                :      */
 9711                          5804                 :          16174 :     NUM_prepare_locale(Np);
                               5805                 :                : 
                               5806                 :                :     /*
                               5807                 :                :      * Processor direct cycle
                               5808                 :                :      */
    7 tgl@sss.pgh.pa.us        5809                 :GNC       16174 :     Np->input_p = Np->input;
                               5810                 :                : 
                               5811         [ +  + ]:          32938 :     for (const FormatNode *n = node; n->type != NODE_TYPE_END; n++)
                               5812                 :                :     {
                               5813                 :                :         /*
                               5814                 :                :          * Check at least one byte remains to be scanned.  (In actions below,
                               5815                 :                :          * must use AMOUNT_TEST if we want to read more bytes than that.)
                               5816                 :                :          */
                               5817         [ +  + ]:          16878 :         if (OVERLOAD_TEST)
                               5818                 :             58 :             break;
                               5819                 :                : 
                               5820                 :                :         /*
                               5821                 :                :          * Format pictures actions
                               5822                 :                :          */
 9633 bruce@momjian.us         5823         [ +  + ]:CBC       16820 :         if (n->type == NODE_TYPE_ACTION)
                               5824                 :                :         {
                               5825                 :                :             /*
                               5826                 :                :              * Create/read digit/zero/blank/sign/special-case
                               5827                 :                :              *
                               5828                 :                :              * 'NUM_S' note: The locale sign is anchored to output and we
                               5829                 :                :              * read/write it when we work with first or last number
                               5830                 :                :              * (NUM_0/NUM_9).  This is why NUM_S is missing in switch().
                               5831                 :                :              *
                               5832                 :                :              * Notice the "Np->input_p++" at the bottom of the loop.  This is
                               5833                 :                :              * why most of the actions advance input_p one less than you might
                               5834                 :                :              * expect.  In cases where we don't want that increment to happen,
                               5835                 :                :              * a switch case ends with "continue" not "break".
                               5836                 :                :              */
                               5837   [ +  +  +  +  :          16760 :             switch (n->key->id)
                                     +  +  -  -  -  
                                              -  + ]
                               5838                 :                :             {
                               5839                 :            582 :                 case NUM_9:
                               5840                 :                :                 case NUM_0:
                               5841                 :                :                 case NUM_DEC:
                               5842                 :                :                 case NUM_D:
    7 tgl@sss.pgh.pa.us        5843                 :GNC         582 :                     NUM_numpart_from_char(Np, n->key->id);
                               5844                 :            582 :                     break;      /* switch() case: */
                               5845                 :                : 
 9633 bruce@momjian.us         5846                 :CBC          24 :                 case NUM_COMMA:
    7 tgl@sss.pgh.pa.us        5847         [ +  - ]:GNC          24 :                     if (!Np->num_in)
                               5848                 :                :                     {
                               5849         [ -  + ]:             24 :                         if (IS_FILLMODE(Np->Num))
 3205 tgl@sss.pgh.pa.us        5850                 :LBC        (24) :                             continue;
                               5851                 :                :                     }
    7 tgl@sss.pgh.pa.us        5852         [ +  - ]:GNC          24 :                     if (*Np->input_p != ',')
                               5853                 :             24 :                         continue;
 9633 bruce@momjian.us         5854                 :LBC       (220) :                     break;
                               5855                 :                : 
 9633 bruce@momjian.us         5856                 :CBC          34 :                 case NUM_G:
 3205 tgl@sss.pgh.pa.us        5857                 :             34 :                     pattern = Np->L_thousands_sep;
                               5858                 :             34 :                     pattern_len = strlen(pattern);
    7 tgl@sss.pgh.pa.us        5859         [ +  - ]:GNC          34 :                     if (!Np->num_in)
                               5860                 :                :                     {
                               5861         [ -  + ]:             34 :                         if (IS_FILLMODE(Np->Num))
 3205 tgl@sss.pgh.pa.us        5862                 :LBC         (4) :                             continue;
                               5863                 :                :                     }
                               5864                 :                : 
                               5865                 :                :                     /*
                               5866                 :                :                      * Because L_thousands_sep typically contains data
                               5867                 :                :                      * characters (either '.' or ','), we can't use
                               5868                 :                :                      * NUM_eat_non_data_chars here.  Instead skip only if the
                               5869                 :                :                      * input matches L_thousands_sep.
                               5870                 :                :                      */
    7 tgl@sss.pgh.pa.us        5871         [ +  - ]:GNC          34 :                     if (AMOUNT_TEST(pattern_len) &&
                               5872         [ +  + ]:             34 :                         strncmp(Np->input_p, pattern, pattern_len) == 0)
                               5873                 :             30 :                         Np->input_p += pattern_len - 1;
                               5874                 :                :                     else
 3205 tgl@sss.pgh.pa.us        5875                 :CBC           4 :                         continue;
 9633 bruce@momjian.us         5876                 :             30 :                     break;
                               5877                 :                : 
    7 tgl@sss.pgh.pa.us        5878                 :GNC          20 :                 case NUM_L:
                               5879                 :             20 :                     pattern = Np->L_currency_symbol;
                               5880                 :             20 :                     NUM_eat_non_data_chars(Np, pg_mbstrlen(pattern));
                               5881                 :             20 :                     continue;
                               5882                 :                : 
 9633 bruce@momjian.us         5883                 :CBC       16072 :                 case NUM_RN:
                               5884                 :                :                 case NUM_rn:
 9031 ishii@postgresql.org     5885                 :GIC       16016 :                     {
    7 tgl@sss.pgh.pa.us        5886                 :GNC       16072 :                         int         roman_result = roman_to_int(Np);
                               5887                 :                :                         int         oldlen;
                               5888                 :                :                         int         numlen;
                               5889                 :                : 
  582 tgl@sss.pgh.pa.us        5890         [ +  + ]:CBC       16072 :                         if (roman_result < 0)
                               5891         [ +  - ]:             56 :                             ereport(ERROR,
                               5892                 :                :                                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                               5893                 :                :                                      errmsg("invalid Roman numeral")));
    7 tgl@sss.pgh.pa.us        5894                 :GNC       16016 :                         oldlen = Np->output->len;
                               5895                 :          16016 :                         NUM_EMITF("%d", roman_result);
                               5896                 :          16016 :                         numlen = Np->output->len - oldlen;
  582 tgl@sss.pgh.pa.us        5897                 :CBC       16016 :                         Np->Num->pre = numlen;
                               5898                 :          16016 :                         Np->Num->post = 0;
                               5899                 :          16016 :                         continue;   /* roman_to_int ate all the chars */
                               5900                 :                :                     }
                               5901                 :                : 
 9633 bruce@momjian.us         5902                 :              4 :                 case NUM_th:
    7 tgl@sss.pgh.pa.us        5903   [ +  -  +  - ]:GNC           4 :                     if (IS_ROMAN(Np->Num) || Np->output->data[0] == '#' ||
 4367 bruce@momjian.us         5904   [ +  -  -  + ]:CBC           4 :                         Np->sign == '-' || IS_DECIMAL(Np->Num))
 9633 bruce@momjian.us         5905                 :LBC        (44) :                         continue;
                               5906                 :                :                     /* All variants of 'th' occupy 2 characters */
    7 tgl@sss.pgh.pa.us        5907                 :GNC           4 :                     NUM_eat_non_data_chars(Np, 2);
                               5908                 :              4 :                     continue;
                               5909                 :                : 
    7 tgl@sss.pgh.pa.us        5910                 :LBC        (60) :                 case NUM_TH:
    7 tgl@sss.pgh.pa.us        5911   [ #  #  #  # ]:UNC           0 :                     if (IS_ROMAN(Np->Num) || Np->output->data[0] == '#' ||
    7 tgl@sss.pgh.pa.us        5912   [ #  #  #  # ]:LBC        (60) :                         Np->sign == '-' || IS_DECIMAL(Np->Num))
                               5913                 :           (44) :                         continue;
                               5914                 :                :                     /* All variants of 'TH' occupy 2 characters */
    7 tgl@sss.pgh.pa.us        5915                 :UNC           0 :                     NUM_eat_non_data_chars(Np, 2);
                               5916                 :              0 :                     continue;
                               5917                 :                : 
    7 tgl@sss.pgh.pa.us        5918                 :LBC       (228) :                 case NUM_MI:
    7 tgl@sss.pgh.pa.us        5919         [ #  # ]:UNC           0 :                     if (*Np->input_p == '-')
                               5920                 :              0 :                         Np->output->data[0] = '-';
                               5921                 :                :                     else
                               5922                 :                :                     {
                               5923                 :              0 :                         NUM_eat_non_data_chars(Np, 1);
 3205                          5924                 :              0 :                         continue;
                               5925                 :                :                     }
 9633 bruce@momjian.us         5926                 :LBC       (228) :                     break;
                               5927                 :                : 
    7 tgl@sss.pgh.pa.us        5928                 :           (20) :                 case NUM_PL:
    7 tgl@sss.pgh.pa.us        5929         [ #  # ]:UNC           0 :                     if (*Np->input_p == '+')
                               5930                 :              0 :                         Np->output->data[0] = '+';
                               5931                 :                :                     else
                               5932                 :                :                     {
                               5933                 :              0 :                         NUM_eat_non_data_chars(Np, 1);
                               5934                 :              0 :                         continue;
                               5935                 :                :                     }
    7 tgl@sss.pgh.pa.us        5936                 :LBC        (20) :                     break;
                               5937                 :                : 
                               5938                 :          (120) :                 case NUM_SG:
    7 tgl@sss.pgh.pa.us        5939         [ #  # ]:UNC           0 :                     if (*Np->input_p == '-')
                               5940                 :              0 :                         Np->output->data[0] = '-';
                               5941         [ #  # ]:              0 :                     else if (*Np->input_p == '+')
                               5942                 :              0 :                         Np->output->data[0] = '+';
                               5943                 :                :                     else
                               5944                 :                :                     {
                               5945                 :              0 :                         NUM_eat_non_data_chars(Np, 1);
 3205                          5946                 :              0 :                         continue;
                               5947                 :                :                     }
 9633 bruce@momjian.us         5948                 :LBC       (120) :                     break;
                               5949                 :                : 
    7 tgl@sss.pgh.pa.us        5950                 :CBC          24 :                 default:
                               5951                 :             24 :                     continue;
                               5952                 :                :                     break;
                               5953                 :                :             }
                               5954                 :                :         }
                               5955                 :                :         else
                               5956                 :                :         {
                               5957                 :                :             /*
                               5958                 :                :              * In TO_NUMBER, we skip one input character for each non-pattern
                               5959                 :                :              * format character, whether or not it matches the format
                               5960                 :                :              * character.
                               5961                 :                :              */
    7 tgl@sss.pgh.pa.us        5962                 :GNC          60 :             Np->input_p += pg_mblen_range(Np->input_p, Np->input_end);
    7 tgl@sss.pgh.pa.us        5963                 :CBC          60 :             continue;
                               5964                 :                :         }
    7 tgl@sss.pgh.pa.us        5965                 :GNC         612 :         Np->input_p++;
                               5966                 :                :     }
                               5967                 :                : 
                               5968                 :                :     /*
                               5969                 :                :      * Truncate any final '.'; we know output string is not empty
                               5970                 :                :      */
                               5971         [ -  + ]:          16118 :     if (Np->output->data[Np->output->len - 1] == '.')
    7 tgl@sss.pgh.pa.us        5972                 :UNC           0 :         Np->output->data[--Np->output->len] = '\0';
                               5973                 :                : 
                               5974                 :                :     /*
                               5975                 :                :      * Correction - precision of dec. number
                               5976                 :                :      */
    7 tgl@sss.pgh.pa.us        5977                 :GNC       16118 :     Np->Num->post = Np->read_post;
                               5978                 :                : 
                               5979                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               5980                 :                :     elog(DEBUG_elog_output, "TO_NUMBER (output): '%s'", Np->output->data);
                               5981                 :                : #endif
                               5982                 :          16118 : }
                               5983                 :                : 
                               5984                 :                : /*
                               5985                 :                :  * Numeric format processing for TO_CHAR.
                               5986                 :                :  *
                               5987                 :                :  * We generate a string in "output" according to the format, working from
                               5988                 :                :  * the datatype-independent representation of the number in "input".
                               5989                 :                :  *
                               5990                 :                :  * node: array of FormatNodes representing the parsed format string
                               5991                 :                :  * Num: input/output argument holding additional format flags and state
                               5992                 :                :  * input: the value to be formatted
                               5993                 :                :  * output: output buffer (results are appended to whatever is there)
                               5994                 :                :  * out_pre_spaces: number of spaces needed before first digit
                               5995                 :                :  * sign: '+' or '-'
                               5996                 :                :  * collid: active collation
                               5997                 :                :  *
                               5998                 :                :  * DOCUMENTME: "input" is mostly in standard format, but the caller is
                               5999                 :                :  * expected to have made some adjustments to it to simplify the logic here.
                               6000                 :                :  * Should reverse-engineer and document the rules.
                               6001                 :                :  */
                               6002                 :                : static void
                               6003                 :         683362 : NUM_processor_to_char(const FormatNode *node, NUMDesc *Num,
                               6004                 :                :                       const char *input, StringInfo output,
                               6005                 :                :                       int out_pre_spaces, int sign, Oid collid)
                               6006                 :                : {
                               6007                 :                :     NUMProc     _Np,
                               6008                 :         683362 :                *Np = &_Np;
                               6009                 :                :     const char *pattern;
                               6010                 :                : 
                               6011   [ +  -  +  -  :       11617154 :     MemSet(Np, 0, sizeof(NUMProc));
                                     +  -  +  -  +  
                                                 + ]
                               6012                 :                : 
                               6013                 :         683362 :     Np->Num = Num;
                               6014                 :         683362 :     Np->input = input;
                               6015                 :         683362 :     Np->output = output;
                               6016                 :         683362 :     Np->last_relevant = NULL;
                               6017                 :         683362 :     Np->out_pre_spaces = out_pre_spaces;
                               6018                 :         683362 :     Np->read_post = 0;
                               6019                 :         683362 :     Np->read_pre = 0;
                               6020                 :         683362 :     Np->read_dec = false;
                               6021                 :                : 
                               6022         [ +  + ]:         683362 :     if (Np->Num->zero_start)
                               6023                 :         665752 :         --Np->Num->zero_start;
                               6024                 :                : 
                               6025         [ +  + ]:         683362 :     if (IS_EEEE(Np->Num))
                               6026                 :                :     {
                               6027                 :                :         /* In EEEE mode, we just regurgitate input as-is */
                               6028                 :            224 :         NUM_EMITS(input);
                               6029                 :            224 :         return;
                               6030                 :                :     }
                               6031                 :                : 
                               6032                 :                :     /*
                               6033                 :                :      * Sign
                               6034                 :                :      */
                               6035                 :         683138 :     Np->sign = sign;
                               6036                 :                : 
                               6037                 :                :     /* MI/PL/SG - write sign itself and not in number */
                               6038   [ +  +  +  + ]:         683138 :     if (IS_PLUS(Np->Num) || IS_MINUS(Np->Num))
                               6039                 :                :     {
                               6040   [ +  +  +  + ]:            368 :         if (IS_PLUS(Np->Num) && IS_MINUS(Np->Num) == false)
                               6041                 :             20 :             Np->sign_wrote = false; /* need sign */
                               6042                 :                :         else
                               6043                 :            348 :             Np->sign_wrote = true;   /* needn't sign */
                               6044                 :                :     }
                               6045                 :                :     else
                               6046                 :                :     {
                               6047         [ +  + ]:         682770 :         if (Np->sign != '-')
                               6048                 :                :         {
                               6049         [ +  + ]:         682421 :             if (IS_FILLMODE(Np->Num))
                               6050                 :         665892 :                 Np->Num->flag &= ~NUM_F_BRACKET;
                               6051                 :                :         }
                               6052                 :                : 
                               6053   [ +  +  +  +  :         682770 :         if (Np->sign == '+' && IS_FILLMODE(Np->Num) && IS_LSIGN(Np->Num) == false)
                                              +  + ]
                               6054                 :         665756 :             Np->sign_wrote = true;   /* needn't sign */
                               6055                 :                :         else
                               6056                 :          17014 :             Np->sign_wrote = false; /* need sign */
                               6057                 :                : 
                               6058   [ +  +  +  + ]:         682770 :         if (Np->Num->lsign == NUM_LSIGN_PRE && Np->Num->pre == Np->Num->pre_lsign_num)
                               6059                 :             20 :             Np->Num->lsign = NUM_LSIGN_POST;
                               6060                 :                :     }
                               6061                 :                : 
                               6062                 :                :     /*
                               6063                 :                :      * Count
                               6064                 :                :      */
                               6065                 :         683138 :     Np->num_count = Np->Num->post + Np->Num->pre - 1;
                               6066                 :                : 
                               6067   [ +  +  +  + ]:         683138 :     if (IS_FILLMODE(Np->Num) && IS_DECIMAL(Np->Num))
                               6068                 :                :     {
                               6069                 :            452 :         Np->last_relevant = get_last_relevant_decnum(Np->input);
                               6070                 :                : 
                               6071                 :                :         /*
                               6072                 :                :          * If any '0' specifiers are present, make sure we don't strip those
                               6073                 :                :          * digits.  But don't advance last_relevant beyond the last character
                               6074                 :                :          * of the Np->input string, which is a hazard if the number got
                               6075                 :                :          * shortened due to precision limitations.
                               6076                 :                :          */
                               6077   [ +  +  +  + ]:            452 :         if (Np->last_relevant && Np->Num->zero_end > Np->out_pre_spaces)
                               6078                 :                :         {
                               6079                 :                :             size_t      last_zero_pos;
                               6080                 :                :             const char *last_zero;
                               6081                 :                : 
                               6082                 :                :             /* note that Np->input cannot be zero-length here */
                               6083                 :            184 :             last_zero_pos = strlen(Np->input) - 1;
                               6084                 :            184 :             last_zero_pos = Min(last_zero_pos,
                               6085                 :                :                                 Np->Num->zero_end - Np->out_pre_spaces);
                               6086                 :            184 :             last_zero = Np->input + last_zero_pos;
                               6087         [ +  + ]:            184 :             if (Np->last_relevant < last_zero)
                               6088                 :             96 :                 Np->last_relevant = last_zero;
                               6089                 :                :         }
                               6090                 :                :     }
                               6091                 :                : 
                               6092   [ +  +  +  + ]:         683138 :     if (Np->sign_wrote == false && Np->out_pre_spaces == 0)
                               6093                 :          16354 :         ++Np->num_count;
                               6094                 :                : 
                               6095                 :         683138 :     Np->num_in = 0;
                               6096                 :         683138 :     Np->num_curr = 0;
                               6097                 :                : 
                               6098                 :                : #ifdef DEBUG_TO_FROM_CHAR
                               6099                 :                :     elog(DEBUG_elog_output,
                               6100                 :                :          "\n\tSIGN: '%c'\n\tNUM: '%s'\n\tPRE: %d\n\tPOST: %d\n\tNUM_COUNT: %d\n\tNUM_PRE: %d\n\tSIGN_WROTE: %s\n\tZERO: %s\n\tZERO_START: %d\n\tZERO_END: %d\n\tLAST_RELEVANT: %s\n\tBRACKET: %s\n\tPLUS: %s\n\tMINUS: %s\n\tFILLMODE: %s\n\tROMAN: %s\n\tEEEE: %s",
                               6101                 :                :          Np->sign,
                               6102                 :                :          Np->input,
                               6103                 :                :          Np->Num->pre,
                               6104                 :                :          Np->Num->post,
                               6105                 :                :          Np->num_count,
                               6106                 :                :          Np->out_pre_spaces,
                               6107                 :                :          Np->sign_wrote ? "Yes" : "No",
                               6108                 :                :          IS_ZERO(Np->Num) ? "Yes" : "No",
                               6109                 :                :          Np->Num->zero_start,
                               6110                 :                :          Np->Num->zero_end,
                               6111                 :                :          Np->last_relevant ? Np->last_relevant : "<not set>",
                               6112                 :                :          IS_BRACKET(Np->Num) ? "Yes" : "No",
                               6113                 :                :          IS_PLUS(Np->Num) ? "Yes" : "No",
                               6114                 :                :          IS_MINUS(Np->Num) ? "Yes" : "No",
                               6115                 :                :          IS_FILLMODE(Np->Num) ? "Yes" : "No",
                               6116                 :                :          IS_ROMAN(Np->Num) ? "Yes" : "No",
                               6117                 :                :          IS_EEEE(Np->Num) ? "Yes" : "No"
                               6118                 :                :         );
                               6119                 :                : #endif
                               6120                 :                : 
                               6121                 :                :     /*
                               6122                 :                :      * Locale
                               6123                 :                :      */
                               6124                 :         683138 :     NUM_prepare_locale(Np);
                               6125                 :                : 
                               6126                 :                :     /*
                               6127                 :                :      * Processor direct cycle
                               6128                 :                :      */
                               6129                 :         683138 :     Np->input_p = Np->input;
                               6130                 :                : 
                               6131         [ +  + ]:        7271840 :     for (const FormatNode *n = node; n->type != NODE_TYPE_END; n++)
                               6132                 :                :     {
                               6133                 :                :         /*
                               6134                 :                :          * Format pictures actions
                               6135                 :                :          */
                               6136         [ +  + ]:        6588702 :         if (n->type == NODE_TYPE_ACTION)
                               6137                 :                :         {
                               6138                 :                :             /*
                               6139                 :                :              * Create/read digit/zero/blank/sign/special-case
                               6140                 :                :              *
                               6141                 :                :              * 'NUM_S' note: The locale sign is anchored to input and we
                               6142                 :                :              * read/write it when we work with first or last number
                               6143                 :                :              * (NUM_0/NUM_9).  This is why NUM_S is missing in switch().
                               6144                 :                :              */
                               6145   [ +  +  +  +  :        6582994 :             switch (n->key->id)
                                     +  +  +  +  +  
                                              +  + ]
                               6146                 :                :             {
                               6147                 :        5898865 :                 case NUM_9:
                               6148                 :                :                 case NUM_0:
                               6149                 :                :                 case NUM_DEC:
                               6150                 :                :                 case NUM_D:
                               6151                 :        5898865 :                     NUM_numpart_to_char(Np, n->key->id);
                               6152                 :        5898865 :                     break;
                               6153                 :                : 
                               6154                 :            220 :                 case NUM_COMMA:
                               6155         [ +  + ]:            220 :                     if (!Np->num_in)
                               6156                 :                :                     {
                               6157         [ +  - ]:             80 :                         if (!IS_FILLMODE(Np->Num))
                               6158                 :             80 :                             NUM_EMITC(' ');
                               6159                 :                :                     }
                               6160                 :                :                     else
                               6161                 :            140 :                         NUM_EMITC(',');
 9633 bruce@momjian.us         6162                 :            220 :                     break;
                               6163                 :                : 
    7 tgl@sss.pgh.pa.us        6164                 :            780 :                 case NUM_G:
                               6165                 :            780 :                     pattern = Np->L_thousands_sep;
                               6166         [ +  + ]:            780 :                     if (!Np->num_in)
                               6167                 :                :                     {
                               6168         [ +  - ]:            392 :                         if (!IS_FILLMODE(Np->Num))
                               6169                 :            392 :                             appendStringInfoSpaces(Np->output,
                               6170                 :                :                                                    pg_mbstrlen(pattern));
                               6171                 :                :                     }
                               6172                 :                :                     else
                               6173                 :                :                     {
                               6174                 :            388 :                         NUM_EMITS(pattern);
                               6175                 :                :                     }
 9633 bruce@momjian.us         6176                 :            780 :                     break;
                               6177                 :                : 
    7 tgl@sss.pgh.pa.us        6178                 :             60 :                 case NUM_L:
                               6179                 :             60 :                     pattern = Np->L_currency_symbol;
                               6180                 :             60 :                     NUM_EMITS(pattern);
                               6181                 :             60 :                     break;
                               6182                 :                : 
                               6183                 :          16088 :                 case NUM_RN:
                               6184                 :                :                 case NUM_rn:
                               6185                 :                :                     {
                               6186                 :                :                         const char *input_p;
                               6187                 :                : 
                               6188         [ +  + ]:          16088 :                         if (n->key->id == NUM_rn)
                               6189                 :             20 :                             input_p = asc_tolower_z(Np->input_p);
                               6190                 :                :                         else
                               6191                 :          16068 :                             input_p = Np->input_p;
                               6192         [ +  + ]:          16088 :                         if (IS_FILLMODE(Np->Num))
                               6193                 :             64 :                             NUM_EMITS(input_p);
                               6194                 :                :                         else
                               6195                 :          16024 :                             NUM_EMITF("%15s", input_p);
                               6196                 :                :                     }
 9633 bruce@momjian.us         6197                 :          16088 :                     break;
                               6198                 :                : 
    7 tgl@sss.pgh.pa.us        6199                 :             60 :                 case NUM_th:
                               6200   [ +  -  +  - ]:             60 :                     if (IS_ROMAN(Np->Num) || *Np->input == '#' ||
                               6201   [ +  +  +  + ]:             60 :                         Np->sign == '-' || IS_DECIMAL(Np->Num))
                               6202                 :                :                         break;
                               6203                 :             16 :                     NUM_EMITS(get_th(Np->input, TH_LOWER));
                               6204                 :             16 :                     break;
                               6205                 :                : 
                               6206                 :             60 :                 case NUM_TH:
                               6207   [ +  -  +  - ]:             60 :                     if (IS_ROMAN(Np->Num) || *Np->input == '#' ||
                               6208   [ +  +  +  + ]:             60 :                         Np->sign == '-' || IS_DECIMAL(Np->Num))
                               6209                 :                :                         break;
                               6210                 :             16 :                     NUM_EMITS(get_th(Np->input, TH_UPPER));
                               6211                 :             16 :                     break;
                               6212                 :                : 
                               6213                 :            228 :                 case NUM_MI:
                               6214         [ +  + ]:            228 :                     if (Np->sign == '-')
                               6215                 :             64 :                         NUM_EMITC('-');
                               6216         [ +  - ]:            164 :                     else if (!IS_FILLMODE(Np->Num))
                               6217                 :            164 :                         NUM_EMITC(' ');
                               6218                 :            228 :                     break;
                               6219                 :                : 
                               6220                 :             20 :                 case NUM_PL:
                               6221         [ +  + ]:             20 :                     if (Np->sign == '+')
                               6222                 :             16 :                         NUM_EMITC('+');
                               6223         [ +  - ]:              4 :                     else if (!IS_FILLMODE(Np->Num))
                               6224                 :              4 :                         NUM_EMITC(' ');
                               6225                 :             20 :                     break;
                               6226                 :                : 
                               6227                 :            120 :                 case NUM_SG:
                               6228                 :            120 :                     NUM_EMITC(Np->sign);
                               6229                 :            120 :                     break;
                               6230                 :                : 
 9633 bruce@momjian.us         6231                 :         666493 :                 default:
                               6232                 :         666493 :                     break;
                               6233                 :                :             }
                               6234                 :                :         }
                               6235                 :                :         else
                               6236                 :                :         {
                               6237                 :                :             /*
                               6238                 :                :              * In TO_CHAR, non-pattern characters in the format are copied to
                               6239                 :                :              * the output.
                               6240                 :                :              */
    7 tgl@sss.pgh.pa.us        6241                 :           5708 :             NUM_EMITS(n->character);
                               6242                 :                :         }
                               6243                 :                :     }
                               6244                 :                : }
                               6245                 :                : 
                               6246                 :                : /*
                               6247                 :                :  * MACRO: Start part of NUM - for all NUM's to_char variants
                               6248                 :                :  *  (sorry, but I hate copy same code - macro is better..)
                               6249                 :                :  */
                               6250                 :                : #define NUM_TOCHAR_prepare \
                               6251                 :                : do { \
                               6252                 :                :     int len = VARSIZE_ANY_EXHDR(fmt); \
                               6253                 :                :     if (len <= 0)        /* easy case for empty format */    \
                               6254                 :                :         PG_RETURN_TEXT_P(cstring_to_text("")); \
                               6255                 :                :     format  = NUM_cache(len, &Num, fmt, &shouldFree);       \
                               6256                 :                : } while (0)
                               6257                 :                : 
                               6258                 :                : /*
                               6259                 :                :  * MACRO: Finish part of NUM
                               6260                 :                :  */
                               6261                 :                : #define NUM_TOCHAR_finish \
                               6262                 :                : do { \
                               6263                 :                :     /*                              \
                               6264                 :                :      * Create workspace to hold result.  We'll use result.data directly as the \
                               6265                 :                :      * returned TEXT datum, so leave enough room for the varlena header. \
                               6266                 :                :      * Temporarily fill that area with spaces; that's not really necessary but \
                               6267                 :                :      * it eases debugging by ensuring the result string is always printable. \
                               6268                 :                :      */                             \
                               6269                 :                :     initStringInfo(&result);        \
                               6270                 :                :     enlargeStringInfo(&result, VARHDRSZ);   /* just pro-forma */            \
                               6271                 :                :     memset(result.data, ' ', VARHDRSZ);                                     \
                               6272                 :                :     result.len = VARHDRSZ;                                                  \
                               6273                 :                :     result.data[VARHDRSZ] = '\0';   /* maintain StringInfo's invariant */   \
                               6274                 :                :                                     \
                               6275                 :                :     NUM_processor_to_char(format, &Num, numstr, &result, \
                               6276                 :                :                           out_pre_spaces, sign, PG_GET_COLLATION()); \
                               6277                 :                :                                     \
                               6278                 :                :     if (shouldFree)                 \
                               6279                 :                :         pfree(format);              \
                               6280                 :                :                                     \
                               6281                 :                :     /*                              \
                               6282                 :                :      * Insert the varlena header needed to make result a valid TEXT datum. \
                               6283                 :                :      * The result is usually much bigger than it needs to be, but there \
                               6284                 :                :      * seems little point in realloc'ing it smaller. \
                               6285                 :                :      */                             \
                               6286                 :                :     SET_VARSIZE(result.data, result.len); \
                               6287                 :                : } while (0)
                               6288                 :                : 
                               6289                 :                : /*
                               6290                 :                :  * NUMERIC to_number() (convert string to numeric)
                               6291                 :                :  */
                               6292                 :                : Datum
 9553 bruce@momjian.us         6293                 :CBC       16182 : numeric_to_number(PG_FUNCTION_ARGS)
                               6294                 :                : {
 3455 noah@leadboat.com        6295                 :          16182 :     text       *value = PG_GETARG_TEXT_PP(0);
                               6296                 :          16182 :     text       *fmt = PG_GETARG_TEXT_PP(1);
                               6297                 :                :     NUMDesc     Num;
                               6298                 :                :     Datum       result;
                               6299                 :                :     FormatNode *format;
                               6300                 :                :     bool        shouldFree;
                               6301                 :                :     StringInfoData numstr;
                               6302                 :                :     int         len;
                               6303                 :                :     int         scale,
                               6304                 :                :                 precision;
                               6305                 :                : 
                               6306                 :          16182 :     len = VARSIZE_ANY_EXHDR(fmt);
                               6307                 :                : 
    7 tgl@sss.pgh.pa.us        6308         [ -  + ]:GNC       16182 :     if (len <= 0)
    7 tgl@sss.pgh.pa.us        6309                 :UNC           0 :         PG_RETURN_NULL();       /* arbitrary choice for empty format */
                               6310                 :                : 
 4367 bruce@momjian.us         6311                 :CBC       16182 :     format = NUM_cache(len, &Num, fmt, &shouldFree);
                               6312                 :                : 
    7 tgl@sss.pgh.pa.us        6313                 :GNC       16174 :     initStringInfo(&numstr);
                               6314                 :                : 
                               6315                 :          32348 :     NUM_processor_from_char(format, &Num,
                               6316                 :          16174 :                             VARDATA_ANY(value), VARSIZE_ANY_EXHDR(value),
                               6317                 :                :                             &numstr,
                               6318                 :                :                             PG_GET_COLLATION());
                               6319                 :                : 
 4367 bruce@momjian.us         6320                 :CBC       16118 :     scale = Num.post;
 3979                          6321                 :          16118 :     precision = Num.pre + Num.multi + scale;
                               6322                 :                : 
 8742                          6323         [ -  + ]:          16118 :     if (shouldFree)
 9660 bruce@momjian.us         6324                 :UBC           0 :         pfree(format);
                               6325                 :                : 
 9553 bruce@momjian.us         6326                 :CBC       16118 :     result = DirectFunctionCall3(numeric_in,
                               6327                 :                :                                  CStringGetDatum(numstr.data),
                               6328                 :                :                                  ObjectIdGetDatum(InvalidOid),
                               6329                 :                :                                  Int32GetDatum(((precision << 16) | scale) + VARHDRSZ));
                               6330                 :                : 
 3979                          6331         [ +  + ]:          16114 :     if (IS_MULTI(&Num))
                               6332                 :                :     {
                               6333                 :                :         Numeric     x;
 2178 peter@eisentraut.org     6334                 :              4 :         Numeric     a = int64_to_numeric(10);
                               6335                 :              4 :         Numeric     b = int64_to_numeric(-Num.multi);
                               6336                 :                : 
 3979 bruce@momjian.us         6337                 :              4 :         x = DatumGetNumeric(DirectFunctionCall2(numeric_power,
                               6338                 :                :                                                 NumericGetDatum(a),
                               6339                 :                :                                                 NumericGetDatum(b)));
                               6340                 :              4 :         result = DirectFunctionCall2(numeric_mul,
                               6341                 :                :                                      result,
                               6342                 :                :                                      NumericGetDatum(x));
                               6343                 :                :     }
                               6344                 :                : 
    7 tgl@sss.pgh.pa.us        6345                 :GNC       16114 :     pfree(numstr.data);
 9660 bruce@momjian.us         6346                 :CBC       16114 :     return result;
                               6347                 :                : }
                               6348                 :                : 
                               6349                 :                : /*
                               6350                 :                :  * NUMERIC to_char()
                               6351                 :                :  */
                               6352                 :                : Datum
 9553                          6353                 :           1201 : numeric_to_char(PG_FUNCTION_ARGS)
                               6354                 :                : {
 9289                          6355                 :           1201 :     Numeric     value = PG_GETARG_NUMERIC(0);
 3455 noah@leadboat.com        6356                 :           1201 :     text       *fmt = PG_GETARG_TEXT_PP(1);
                               6357                 :                :     NUMDesc     Num;
                               6358                 :                :     FormatNode *format;
                               6359                 :                :     StringInfoData result;
                               6360                 :                :     bool        shouldFree;
 4374 bruce@momjian.us         6361                 :           1201 :     int         out_pre_spaces = 0,
 9289                          6362                 :           1201 :                 sign = 0;
                               6363                 :                :     char       *numstr,
                               6364                 :                :                *orgnum,
                               6365                 :                :                *p;
                               6366                 :                : 
 9711                          6367   [ -  +  -  + ]:           1201 :     NUM_TOCHAR_prepare;
                               6368                 :                : 
                               6369                 :                :     /*
                               6370                 :                :      * On DateType depend part (numeric)
                               6371                 :                :      */
 4367                          6372         [ +  + ]:           1201 :     if (IS_ROMAN(&Num))
                               6373                 :                :     {
                               6374                 :                :         int32       intvalue;
  356 michael@paquier.xyz      6375                 :             52 :         ErrorSaveContext escontext = {T_ErrorSaveContext};
                               6376                 :                : 
                               6377                 :                :         /* Round and convert to int */
                               6378                 :             52 :         intvalue = numeric_int4_safe(value, (Node *) &escontext);
                               6379                 :                :         /* On overflow, just use PG_INT32_MAX; int_to_roman will cope */
                               6380         [ +  + ]:             52 :         if (escontext.error_occurred)
  700 tgl@sss.pgh.pa.us        6381                 :              4 :             intvalue = PG_INT32_MAX;
                               6382                 :             52 :         numstr = int_to_roman(intvalue);
                               6383                 :                :     }
 4367 bruce@momjian.us         6384         [ +  + ]:           1149 :     else if (IS_EEEE(&Num))
                               6385                 :                :     {
                               6386                 :            156 :         orgnum = numeric_out_sci(value, Num.post);
                               6387                 :                : 
                               6388                 :                :         /*
                               6389                 :                :          * numeric_out_sci() does not emit a sign for positive numbers.  We
                               6390                 :                :          * need to add a space in this case so that positive and negative
                               6391                 :                :          * numbers are aligned.  Also must check for NaN/infinity cases, which
                               6392                 :                :          * we handle the same way as in float8_to_char.
                               6393                 :                :          */
 2227 tgl@sss.pgh.pa.us        6394         [ +  + ]:            156 :         if (strcmp(orgnum, "NaN") == 0 ||
                               6395         [ +  + ]:            152 :             strcmp(orgnum, "Infinity") == 0 ||
                               6396         [ +  + ]:            148 :             strcmp(orgnum, "-Infinity") == 0)
                               6397                 :                :         {
                               6398                 :                :             /*
                               6399                 :                :              * Allow 6 characters for the leading sign, the decimal point,
                               6400                 :                :              * "e", the exponent's sign and two exponent digits.
                               6401                 :                :              */
 4367 bruce@momjian.us         6402                 :             12 :             numstr = (char *) palloc(Num.pre + Num.post + 7);
                               6403                 :             12 :             fill_str(numstr, '#', Num.pre + Num.post + 6);
 6226 tgl@sss.pgh.pa.us        6404                 :             12 :             *numstr = ' ';
 4367 bruce@momjian.us         6405                 :             12 :             *(numstr + Num.pre + 1) = '.';
                               6406                 :                :         }
 6226 tgl@sss.pgh.pa.us        6407         [ +  + ]:            144 :         else if (*orgnum != '-')
                               6408                 :                :         {
                               6409                 :            128 :             numstr = (char *) palloc(strlen(orgnum) + 2);
                               6410                 :            128 :             *numstr = ' ';
                               6411                 :            128 :             strcpy(numstr + 1, orgnum);
                               6412                 :                :         }
                               6413                 :                :         else
                               6414                 :                :         {
                               6415                 :             16 :             numstr = orgnum;
                               6416                 :                :         }
                               6417                 :                :     }
                               6418                 :                :     else
                               6419                 :                :     {
                               6420                 :                :         size_t      numstr_pre_len;
 9633 bruce@momjian.us         6421                 :            993 :         Numeric     val = value;
                               6422                 :                :         Numeric     x;
                               6423                 :                : 
 4367                          6424         [ +  + ]:            993 :         if (IS_MULTI(&Num))
                               6425                 :                :         {
 2178 peter@eisentraut.org     6426                 :              4 :             Numeric     a = int64_to_numeric(10);
                               6427                 :              4 :             Numeric     b = int64_to_numeric(Num.multi);
                               6428                 :                : 
 9525 tgl@sss.pgh.pa.us        6429                 :              4 :             x = DatumGetNumeric(DirectFunctionCall2(numeric_power,
                               6430                 :                :                                                     NumericGetDatum(a),
                               6431                 :                :                                                     NumericGetDatum(b)));
                               6432                 :              4 :             val = DatumGetNumeric(DirectFunctionCall2(numeric_mul,
                               6433                 :                :                                                       NumericGetDatum(value),
                               6434                 :                :                                                       NumericGetDatum(x)));
 4367 bruce@momjian.us         6435                 :              4 :             Num.pre += Num.multi;
                               6436                 :                :         }
                               6437                 :                : 
 9553                          6438                 :            993 :         x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
                               6439                 :                :                                                 NumericGetDatum(val),
                               6440                 :                :                                                 Int32GetDatum(Num.post)));
                               6441                 :            993 :         orgnum = DatumGetCString(DirectFunctionCall1(numeric_out,
                               6442                 :                :                                                      NumericGetDatum(x)));
                               6443                 :                : 
 9633                          6444         [ +  + ]:            993 :         if (*orgnum == '-')
                               6445                 :                :         {
 9711                          6446                 :            281 :             sign = '-';
 9633                          6447                 :            281 :             numstr = orgnum + 1;
                               6448                 :                :         }
                               6449                 :                :         else
                               6450                 :                :         {
 9711                          6451                 :            712 :             sign = '+';
                               6452                 :            712 :             numstr = orgnum;
                               6453                 :                :         }
                               6454                 :                : 
                               6455         [ +  + ]:            993 :         if ((p = strchr(numstr, '.')))
 4374                          6456                 :            797 :             numstr_pre_len = p - numstr;
                               6457                 :                :         else
                               6458                 :            196 :             numstr_pre_len = strlen(numstr);
                               6459                 :                : 
                               6460                 :                :         /* needs padding? */
 4367                          6461         [ +  + ]:            993 :         if (numstr_pre_len < Num.pre)
                               6462                 :            920 :             out_pre_spaces = Num.pre - numstr_pre_len;
                               6463                 :                :         /* overflowed prefix digit format? */
                               6464         [ +  + ]:             73 :         else if (numstr_pre_len > Num.pre)
                               6465                 :                :         {
                               6466                 :             20 :             numstr = (char *) palloc(Num.pre + Num.post + 2);
                               6467                 :             20 :             fill_str(numstr, '#', Num.pre + Num.post + 1);
                               6468                 :             20 :             *(numstr + Num.pre) = '.';
                               6469                 :                :         }
                               6470                 :                :     }
                               6471                 :                : 
 9711                          6472         [ +  + ]:           1201 :     NUM_TOCHAR_finish;
    7 tgl@sss.pgh.pa.us        6473                 :GNC        1201 :     PG_RETURN_TEXT_P((text *) result.data);
                               6474                 :                : }
                               6475                 :                : 
                               6476                 :                : /*
                               6477                 :                :  * INT4 to_char()
                               6478                 :                :  */
                               6479                 :                : Datum
 9553 bruce@momjian.us         6480                 :CBC      681477 : int4_to_char(PG_FUNCTION_ARGS)
                               6481                 :                : {
 9289                          6482                 :         681477 :     int32       value = PG_GETARG_INT32(0);
 3455 noah@leadboat.com        6483                 :         681477 :     text       *fmt = PG_GETARG_TEXT_PP(1);
                               6484                 :                :     NUMDesc     Num;
                               6485                 :                :     FormatNode *format;
                               6486                 :                :     StringInfoData result;
                               6487                 :                :     bool        shouldFree;
 4374 bruce@momjian.us         6488                 :         681477 :     int         out_pre_spaces = 0,
 9289                          6489                 :         681477 :                 sign = 0;
                               6490                 :                :     char       *numstr,
                               6491                 :                :                *orgnum;
                               6492                 :                : 
 9711                          6493   [ -  +  -  + ]:         681477 :     NUM_TOCHAR_prepare;
                               6494                 :                : 
                               6495                 :                :     /*
                               6496                 :                :      * On DateType depend part (int32)
                               6497                 :                :      */
 4367                          6498         [ +  + ]:         681477 :     if (IS_ROMAN(&Num))
 2182 tgl@sss.pgh.pa.us        6499                 :          15996 :         numstr = int_to_roman(value);
 4367 bruce@momjian.us         6500         [ +  + ]:         665481 :     else if (IS_EEEE(&Num))
                               6501                 :                :     {
                               6502                 :                :         /* we can do it easily because float8 won't lose any precision */
 6026                          6503                 :              4 :         float8      val = (float8) value;
                               6504                 :                : 
 3087 peter_e@gmx.net          6505                 :              4 :         orgnum = (char *) psprintf("%+.*e", Num.post, val);
                               6506                 :                : 
                               6507                 :                :         /*
                               6508                 :                :          * Swap a leading positive sign for a space.
                               6509                 :                :          */
 6226 tgl@sss.pgh.pa.us        6510         [ +  - ]:              4 :         if (*orgnum == '+')
                               6511                 :              4 :             *orgnum = ' ';
                               6512                 :                : 
                               6513                 :              4 :         numstr = orgnum;
                               6514                 :                :     }
                               6515                 :                :     else
                               6516                 :                :     {
                               6517                 :                :         size_t      numstr_pre_len;
                               6518                 :                : 
 4367 bruce@momjian.us         6519         [ +  + ]:         665477 :         if (IS_MULTI(&Num))
                               6520                 :                :         {
 9579 tgl@sss.pgh.pa.us        6521                 :              4 :             orgnum = DatumGetCString(DirectFunctionCall1(int4out,
                               6522                 :                :                                                          Int32GetDatum(value * ((int32) pow((double) 10, (double) Num.multi)))));
 4367 bruce@momjian.us         6523                 :              4 :             Num.pre += Num.multi;
                               6524                 :                :         }
                               6525                 :                :         else
                               6526                 :                :         {
 9579 tgl@sss.pgh.pa.us        6527                 :         665473 :             orgnum = DatumGetCString(DirectFunctionCall1(int4out,
                               6528                 :                :                                                          Int32GetDatum(value)));
                               6529                 :                :         }
                               6530                 :                : 
 9633 bruce@momjian.us         6531         [ -  + ]:         665477 :         if (*orgnum == '-')
                               6532                 :                :         {
 9711 bruce@momjian.us         6533                 :UBC           0 :             sign = '-';
 6999 tgl@sss.pgh.pa.us        6534                 :              0 :             orgnum++;
                               6535                 :                :         }
                               6536                 :                :         else
 9711 bruce@momjian.us         6537                 :CBC      665477 :             sign = '+';
                               6538                 :                : 
 4374                          6539                 :         665477 :         numstr_pre_len = strlen(orgnum);
                               6540                 :                : 
                               6541                 :                :         /* post-decimal digits?  Pad out with zeros. */
 4367                          6542         [ -  + ]:         665477 :         if (Num.post)
                               6543                 :                :         {
 4367 bruce@momjian.us         6544                 :UBC           0 :             numstr = (char *) palloc(numstr_pre_len + Num.post + 2);
 6999 tgl@sss.pgh.pa.us        6545                 :              0 :             strcpy(numstr, orgnum);
 4374 bruce@momjian.us         6546                 :              0 :             *(numstr + numstr_pre_len) = '.';
 4367                          6547                 :              0 :             memset(numstr + numstr_pre_len + 1, '0', Num.post);
                               6548                 :              0 :             *(numstr + numstr_pre_len + Num.post + 1) = '\0';
                               6549                 :                :         }
                               6550                 :                :         else
 6999 tgl@sss.pgh.pa.us        6551                 :CBC      665477 :             numstr = orgnum;
                               6552                 :                : 
                               6553                 :                :         /* needs padding? */
 4367 bruce@momjian.us         6554         [ +  + ]:         665477 :         if (numstr_pre_len < Num.pre)
                               6555                 :         655941 :             out_pre_spaces = Num.pre - numstr_pre_len;
                               6556                 :                :         /* overflowed prefix digit format? */
                               6557         [ -  + ]:           9536 :         else if (numstr_pre_len > Num.pre)
                               6558                 :                :         {
 4367 bruce@momjian.us         6559                 :UBC           0 :             numstr = (char *) palloc(Num.pre + Num.post + 2);
                               6560                 :              0 :             fill_str(numstr, '#', Num.pre + Num.post + 1);
                               6561                 :              0 :             *(numstr + Num.pre) = '.';
                               6562                 :                :         }
                               6563                 :                :     }
                               6564                 :                : 
 9711 bruce@momjian.us         6565         [ -  + ]:CBC      681477 :     NUM_TOCHAR_finish;
    7 tgl@sss.pgh.pa.us        6566                 :GNC      681477 :     PG_RETURN_TEXT_P((text *) result.data);
                               6567                 :                : }
                               6568                 :                : 
                               6569                 :                : /*
                               6570                 :                :  * INT8 to_char()
                               6571                 :                :  */
                               6572                 :                : Datum
 9553 bruce@momjian.us         6573                 :CBC         473 : int8_to_char(PG_FUNCTION_ARGS)
                               6574                 :                : {
 9289                          6575                 :            473 :     int64       value = PG_GETARG_INT64(0);
 3455 noah@leadboat.com        6576                 :            473 :     text       *fmt = PG_GETARG_TEXT_PP(1);
                               6577                 :                :     NUMDesc     Num;
                               6578                 :                :     FormatNode *format;
                               6579                 :                :     StringInfoData result;
                               6580                 :                :     bool        shouldFree;
 4374 bruce@momjian.us         6581                 :            473 :     int         out_pre_spaces = 0,
 9289                          6582                 :            473 :                 sign = 0;
                               6583                 :                :     char       *numstr,
                               6584                 :                :                *orgnum;
                               6585                 :                : 
 9711                          6586   [ -  +  -  + ]:            473 :     NUM_TOCHAR_prepare;
                               6587                 :                : 
                               6588                 :                :     /*
                               6589                 :                :      * On DateType depend part (int64)
                               6590                 :                :      */
 4367                          6591         [ +  + ]:            473 :     if (IS_ROMAN(&Num))
                               6592                 :                :     {
                               6593                 :                :         int32       intvalue;
                               6594                 :                : 
                               6595                 :                :         /* On overflow, just use PG_INT32_MAX; int_to_roman will cope */
  700 tgl@sss.pgh.pa.us        6596   [ +  +  +  + ]:             20 :         if (value <= PG_INT32_MAX && value >= PG_INT32_MIN)
                               6597                 :              8 :             intvalue = (int32) value;
                               6598                 :                :         else
                               6599                 :             12 :             intvalue = PG_INT32_MAX;
                               6600                 :             20 :         numstr = int_to_roman(intvalue);
                               6601                 :                :     }
 4367 bruce@momjian.us         6602         [ +  + ]:            453 :     else if (IS_EEEE(&Num))
                               6603                 :                :     {
                               6604                 :                :         /* to avoid loss of precision, must go via numeric not float8 */
 2178 peter@eisentraut.org     6605                 :              8 :         orgnum = numeric_out_sci(int64_to_numeric(value),
                               6606                 :                :                                  Num.post);
                               6607                 :                : 
                               6608                 :                :         /*
                               6609                 :                :          * numeric_out_sci() does not emit a sign for positive numbers.  We
                               6610                 :                :          * need to add a space in this case so that positive and negative
                               6611                 :                :          * numbers are aligned.  We don't have to worry about NaN/inf here.
                               6612                 :                :          */
 6226 tgl@sss.pgh.pa.us        6613         [ +  + ]:              8 :         if (*orgnum != '-')
                               6614                 :                :         {
                               6615                 :              4 :             numstr = (char *) palloc(strlen(orgnum) + 2);
                               6616                 :              4 :             *numstr = ' ';
                               6617                 :              4 :             strcpy(numstr + 1, orgnum);
                               6618                 :                :         }
                               6619                 :                :         else
                               6620                 :                :         {
                               6621                 :              4 :             numstr = orgnum;
                               6622                 :                :         }
                               6623                 :                :     }
                               6624                 :                :     else
                               6625                 :                :     {
                               6626                 :                :         size_t      numstr_pre_len;
                               6627                 :                : 
 4367 bruce@momjian.us         6628         [ +  + ]:            445 :         if (IS_MULTI(&Num))
                               6629                 :                :         {
                               6630                 :              4 :             double      multi = pow((double) 10, (double) Num.multi);
                               6631                 :                : 
 9553                          6632                 :              4 :             value = DatumGetInt64(DirectFunctionCall2(int8mul,
                               6633                 :                :                                                       Int64GetDatum(value),
                               6634                 :                :                                                       DirectFunctionCall1(dtoi8,
                               6635                 :                :                                                                           Float8GetDatum(multi))));
 4367                          6636                 :              4 :             Num.pre += Num.multi;
                               6637                 :                :         }
                               6638                 :                : 
 9553                          6639                 :            445 :         orgnum = DatumGetCString(DirectFunctionCall1(int8out,
                               6640                 :                :                                                      Int64GetDatum(value)));
                               6641                 :                : 
 9633                          6642         [ +  + ]:            445 :         if (*orgnum == '-')
                               6643                 :                :         {
 9711                          6644                 :            136 :             sign = '-';
 6999 tgl@sss.pgh.pa.us        6645                 :            136 :             orgnum++;
                               6646                 :                :         }
                               6647                 :                :         else
 9711 bruce@momjian.us         6648                 :            309 :             sign = '+';
                               6649                 :                : 
 4374                          6650                 :            445 :         numstr_pre_len = strlen(orgnum);
                               6651                 :                : 
                               6652                 :                :         /* post-decimal digits?  Pad out with zeros. */
 4367                          6653         [ +  + ]:            445 :         if (Num.post)
                               6654                 :                :         {
                               6655                 :            140 :             numstr = (char *) palloc(numstr_pre_len + Num.post + 2);
 6999 tgl@sss.pgh.pa.us        6656                 :            140 :             strcpy(numstr, orgnum);
 4374 bruce@momjian.us         6657                 :            140 :             *(numstr + numstr_pre_len) = '.';
 4367                          6658                 :            140 :             memset(numstr + numstr_pre_len + 1, '0', Num.post);
                               6659                 :            140 :             *(numstr + numstr_pre_len + Num.post + 1) = '\0';
                               6660                 :                :         }
                               6661                 :                :         else
 6999 tgl@sss.pgh.pa.us        6662                 :            305 :             numstr = orgnum;
                               6663                 :                : 
                               6664                 :                :         /* needs padding? */
 4367 bruce@momjian.us         6665         [ +  + ]:            445 :         if (numstr_pre_len < Num.pre)
                               6666                 :            180 :             out_pre_spaces = Num.pre - numstr_pre_len;
                               6667                 :                :         /* overflowed prefix digit format? */
                               6668         [ -  + ]:            265 :         else if (numstr_pre_len > Num.pre)
                               6669                 :                :         {
 4367 bruce@momjian.us         6670                 :UBC           0 :             numstr = (char *) palloc(Num.pre + Num.post + 2);
                               6671                 :              0 :             fill_str(numstr, '#', Num.pre + Num.post + 1);
                               6672                 :              0 :             *(numstr + Num.pre) = '.';
                               6673                 :                :         }
                               6674                 :                :     }
                               6675                 :                : 
 9711 bruce@momjian.us         6676         [ +  + ]:CBC         473 :     NUM_TOCHAR_finish;
    7 tgl@sss.pgh.pa.us        6677                 :GNC         473 :     PG_RETURN_TEXT_P((text *) result.data);
                               6678                 :                : }
                               6679                 :                : 
                               6680                 :                : /*
                               6681                 :                :  * FLOAT4 to_char()
                               6682                 :                :  */
                               6683                 :                : Datum
 9553 bruce@momjian.us         6684                 :CBC          98 : float4_to_char(PG_FUNCTION_ARGS)
                               6685                 :                : {
 9289                          6686                 :             98 :     float4      value = PG_GETARG_FLOAT4(0);
 3455 noah@leadboat.com        6687                 :             98 :     text       *fmt = PG_GETARG_TEXT_PP(1);
                               6688                 :                :     NUMDesc     Num;
                               6689                 :                :     FormatNode *format;
                               6690                 :                :     StringInfoData result;
                               6691                 :                :     bool        shouldFree;
 4374 bruce@momjian.us         6692                 :             98 :     int         out_pre_spaces = 0,
 9289                          6693                 :             98 :                 sign = 0;
                               6694                 :                :     char       *numstr,
                               6695                 :                :                *p;
                               6696                 :                : 
 9711                          6697   [ -  +  -  + ]:             98 :     NUM_TOCHAR_prepare;
                               6698                 :                : 
 4367                          6699         [ +  + ]:             98 :     if (IS_ROMAN(&Num))
                               6700                 :                :     {
                               6701                 :                :         int32       intvalue;
                               6702                 :                : 
                               6703                 :                :         /* See notes in ftoi4() */
  700 tgl@sss.pgh.pa.us        6704                 :              8 :         value = rint(value);
                               6705                 :                :         /* On overflow, just use PG_INT32_MAX; int_to_roman will cope */
                               6706   [ +  -  +  -  :              8 :         if (!isnan(value) && FLOAT4_FITS_IN_INT32(value))
                                              +  + ]
                               6707                 :              4 :             intvalue = (int32) value;
                               6708                 :                :         else
                               6709                 :              4 :             intvalue = PG_INT32_MAX;
                               6710                 :              8 :         numstr = int_to_roman(intvalue);
                               6711                 :                :     }
 4367 bruce@momjian.us         6712         [ +  + ]:             90 :     else if (IS_EEEE(&Num))
                               6713                 :                :     {
 2882 tgl@sss.pgh.pa.us        6714   [ +  +  +  + ]:             28 :         if (isnan(value) || isinf(value))
                               6715                 :                :         {
                               6716                 :                :             /*
                               6717                 :                :              * Allow 6 characters for the leading sign, the decimal point,
                               6718                 :                :              * "e", the exponent's sign and two exponent digits.
                               6719                 :                :              */
 4367 bruce@momjian.us         6720                 :             12 :             numstr = (char *) palloc(Num.pre + Num.post + 7);
                               6721                 :             12 :             fill_str(numstr, '#', Num.pre + Num.post + 6);
 6226 tgl@sss.pgh.pa.us        6722                 :             12 :             *numstr = ' ';
 4367 bruce@momjian.us         6723                 :             12 :             *(numstr + Num.pre + 1) = '.';
                               6724                 :                :         }
                               6725                 :                :         else
                               6726                 :                :         {
 2183 tgl@sss.pgh.pa.us        6727                 :             16 :             numstr = psprintf("%+.*e", Num.post, value);
                               6728                 :                : 
                               6729                 :                :             /*
                               6730                 :                :              * Swap a leading positive sign for a space.
                               6731                 :                :              */
                               6732         [ +  + ]:             16 :             if (*numstr == '+')
                               6733                 :             12 :                 *numstr = ' ';
                               6734                 :                :         }
                               6735                 :                :     }
                               6736                 :                :     else
                               6737                 :                :     {
 9553 bruce@momjian.us         6738                 :             62 :         float4      val = value;
                               6739                 :                :         char       *orgnum;
                               6740                 :                :         size_t      numstr_pre_len;
                               6741                 :                : 
 4367                          6742         [ +  + ]:             62 :         if (IS_MULTI(&Num))
                               6743                 :                :         {
                               6744                 :              4 :             float       multi = pow((double) 10, (double) Num.multi);
                               6745                 :                : 
 9553                          6746                 :              4 :             val = value * multi;
 4367                          6747                 :              4 :             Num.pre += Num.multi;
                               6748                 :                :         }
                               6749                 :                : 
 2183 tgl@sss.pgh.pa.us        6750                 :             62 :         orgnum = psprintf("%.0f", fabs(val));
 4176 bruce@momjian.us         6751                 :             62 :         numstr_pre_len = strlen(orgnum);
                               6752                 :                : 
                               6753                 :                :         /* adjust post digits to fit max float digits */
                               6754         [ +  + ]:             62 :         if (numstr_pre_len >= FLT_DIG)
                               6755                 :             28 :             Num.post = 0;
                               6756         [ -  + ]:             34 :         else if (numstr_pre_len + Num.post > FLT_DIG)
 4176 bruce@momjian.us         6757                 :UBC           0 :             Num.post = FLT_DIG - numstr_pre_len;
 3087 peter_e@gmx.net          6758                 :CBC          62 :         orgnum = psprintf("%.*f", Num.post, val);
                               6759                 :                : 
 9633 bruce@momjian.us         6760         [ +  + ]:             62 :         if (*orgnum == '-')
                               6761                 :                :         {                       /* < 0 */
 9711                          6762                 :             16 :             sign = '-';
 9633                          6763                 :             16 :             numstr = orgnum + 1;
                               6764                 :                :         }
                               6765                 :                :         else
                               6766                 :                :         {
 9711                          6767                 :             46 :             sign = '+';
                               6768                 :             46 :             numstr = orgnum;
                               6769                 :                :         }
                               6770                 :                : 
                               6771         [ +  + ]:             62 :         if ((p = strchr(numstr, '.')))
 4374                          6772                 :             26 :             numstr_pre_len = p - numstr;
                               6773                 :                :         else
                               6774                 :             36 :             numstr_pre_len = strlen(numstr);
                               6775                 :                : 
                               6776                 :                :         /* needs padding? */
 4367                          6777         [ +  + ]:             62 :         if (numstr_pre_len < Num.pre)
                               6778                 :             40 :             out_pre_spaces = Num.pre - numstr_pre_len;
                               6779                 :                :         /* overflowed prefix digit format? */
                               6780         [ +  + ]:             22 :         else if (numstr_pre_len > Num.pre)
                               6781                 :                :         {
                               6782                 :             16 :             numstr = (char *) palloc(Num.pre + Num.post + 2);
                               6783                 :             16 :             fill_str(numstr, '#', Num.pre + Num.post + 1);
                               6784                 :             16 :             *(numstr + Num.pre) = '.';
                               6785                 :                :         }
                               6786                 :                :     }
                               6787                 :                : 
 9711                          6788         [ -  + ]:             98 :     NUM_TOCHAR_finish;
    7 tgl@sss.pgh.pa.us        6789                 :GNC          98 :     PG_RETURN_TEXT_P((text *) result.data);
                               6790                 :                : }
                               6791                 :                : 
                               6792                 :                : /*
                               6793                 :                :  * FLOAT8 to_char()
                               6794                 :                :  */
                               6795                 :                : Datum
 9553 bruce@momjian.us         6796                 :CBC         113 : float8_to_char(PG_FUNCTION_ARGS)
                               6797                 :                : {
 9289                          6798                 :            113 :     float8      value = PG_GETARG_FLOAT8(0);
 3455 noah@leadboat.com        6799                 :            113 :     text       *fmt = PG_GETARG_TEXT_PP(1);
                               6800                 :                :     NUMDesc     Num;
                               6801                 :                :     FormatNode *format;
                               6802                 :                :     StringInfoData result;
                               6803                 :                :     bool        shouldFree;
 4374 bruce@momjian.us         6804                 :            113 :     int         out_pre_spaces = 0,
 9289                          6805                 :            113 :                 sign = 0;
                               6806                 :                :     char       *numstr,
                               6807                 :                :                *p;
                               6808                 :                : 
 9711                          6809   [ -  +  -  + ]:            113 :     NUM_TOCHAR_prepare;
                               6810                 :                : 
 4367                          6811         [ +  + ]:            113 :     if (IS_ROMAN(&Num))
                               6812                 :                :     {
                               6813                 :                :         int32       intvalue;
                               6814                 :                : 
                               6815                 :                :         /* See notes in dtoi4() */
  700 tgl@sss.pgh.pa.us        6816                 :             12 :         value = rint(value);
                               6817                 :                :         /* On overflow, just use PG_INT32_MAX; int_to_roman will cope */
                               6818   [ +  -  +  -  :             12 :         if (!isnan(value) && FLOAT8_FITS_IN_INT32(value))
                                              +  + ]
                               6819                 :              8 :             intvalue = (int32) value;
                               6820                 :                :         else
                               6821                 :              4 :             intvalue = PG_INT32_MAX;
                               6822                 :             12 :         numstr = int_to_roman(intvalue);
                               6823                 :                :     }
 4367 bruce@momjian.us         6824         [ +  + ]:            101 :     else if (IS_EEEE(&Num))
                               6825                 :                :     {
 2882 tgl@sss.pgh.pa.us        6826   [ +  +  +  + ]:             28 :         if (isnan(value) || isinf(value))
                               6827                 :                :         {
                               6828                 :                :             /*
                               6829                 :                :              * Allow 6 characters for the leading sign, the decimal point,
                               6830                 :                :              * "e", the exponent's sign and two exponent digits.
                               6831                 :                :              */
 4367 bruce@momjian.us         6832                 :             12 :             numstr = (char *) palloc(Num.pre + Num.post + 7);
                               6833                 :             12 :             fill_str(numstr, '#', Num.pre + Num.post + 6);
 6226 tgl@sss.pgh.pa.us        6834                 :             12 :             *numstr = ' ';
 4367 bruce@momjian.us         6835                 :             12 :             *(numstr + Num.pre + 1) = '.';
                               6836                 :                :         }
                               6837                 :                :         else
                               6838                 :                :         {
 2183 tgl@sss.pgh.pa.us        6839                 :             16 :             numstr = psprintf("%+.*e", Num.post, value);
                               6840                 :                : 
                               6841                 :                :             /*
                               6842                 :                :              * Swap a leading positive sign for a space.
                               6843                 :                :              */
                               6844         [ +  + ]:             16 :             if (*numstr == '+')
                               6845                 :             12 :                 *numstr = ' ';
                               6846                 :                :         }
                               6847                 :                :     }
                               6848                 :                :     else
                               6849                 :                :     {
 9553 bruce@momjian.us         6850                 :             73 :         float8      val = value;
                               6851                 :                :         char       *orgnum;
                               6852                 :                :         size_t      numstr_pre_len;
                               6853                 :                : 
 4367                          6854         [ +  + ]:             73 :         if (IS_MULTI(&Num))
                               6855                 :                :         {
                               6856                 :              4 :             double      multi = pow((double) 10, (double) Num.multi);
                               6857                 :                : 
 9553                          6858                 :              4 :             val = value * multi;
 4367                          6859                 :              4 :             Num.pre += Num.multi;
                               6860                 :                :         }
                               6861                 :                : 
 3087 peter_e@gmx.net          6862                 :             73 :         orgnum = psprintf("%.0f", fabs(val));
                               6863                 :             73 :         numstr_pre_len = strlen(orgnum);
                               6864                 :                : 
                               6865                 :                :         /* adjust post digits to fit max double digits */
 4176 bruce@momjian.us         6866         [ +  + ]:             73 :         if (numstr_pre_len >= DBL_DIG)
                               6867                 :              4 :             Num.post = 0;
                               6868         [ +  + ]:             69 :         else if (numstr_pre_len + Num.post > DBL_DIG)
                               6869                 :              4 :             Num.post = DBL_DIG - numstr_pre_len;
 3087 peter_e@gmx.net          6870                 :             73 :         orgnum = psprintf("%.*f", Num.post, val);
                               6871                 :                : 
 9633 bruce@momjian.us         6872         [ +  + ]:             73 :         if (*orgnum == '-')
                               6873                 :                :         {                       /* < 0 */
 9711                          6874                 :             16 :             sign = '-';
 9633                          6875                 :             16 :             numstr = orgnum + 1;
                               6876                 :                :         }
                               6877                 :                :         else
                               6878                 :                :         {
 9711                          6879                 :             57 :             sign = '+';
                               6880                 :             57 :             numstr = orgnum;
                               6881                 :                :         }
                               6882                 :                : 
                               6883         [ +  + ]:             73 :         if ((p = strchr(numstr, '.')))
 4374                          6884                 :             41 :             numstr_pre_len = p - numstr;
                               6885                 :                :         else
                               6886                 :             32 :             numstr_pre_len = strlen(numstr);
                               6887                 :                : 
                               6888                 :                :         /* needs padding? */
 4367                          6889         [ +  + ]:             73 :         if (numstr_pre_len < Num.pre)
                               6890                 :             44 :             out_pre_spaces = Num.pre - numstr_pre_len;
                               6891                 :                :         /* overflowed prefix digit format? */
                               6892         [ +  + ]:             29 :         else if (numstr_pre_len > Num.pre)
                               6893                 :                :         {
                               6894                 :             20 :             numstr = (char *) palloc(Num.pre + Num.post + 2);
                               6895                 :             20 :             fill_str(numstr, '#', Num.pre + Num.post + 1);
                               6896                 :             20 :             *(numstr + Num.pre) = '.';
                               6897                 :                :         }
                               6898                 :                :     }
                               6899                 :                : 
 9711                          6900         [ -  + ]:            113 :     NUM_TOCHAR_finish;
    7 tgl@sss.pgh.pa.us        6901                 :GNC         113 :     PG_RETURN_TEXT_P((text *) result.data);
                               6902                 :                : }
        

Generated by: LCOV version 2.0-1