LCOV - code coverage report
Current view: top level - src/backend/utils/adt - formatting.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 1791 2306 77.7 %
Date: 2023-05-31 04:12:22 Functions: 59 64 92.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14