LCOV - code coverage report
Current view: top level - src/backend/utils/adt - formatting.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 1443 1939 74.4 %
Date: 2019-06-18 07:06:57 Functions: 55 59 93.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13