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

Generated by: LCOV version 1.11