Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Enumerations | Functions | Variables

formatting.c File Reference

#include "postgres.h"
#include <ctype.h>
#include <unistd.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include "catalog/pg_collation.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
#include "utils/formatting.h"
#include "utils/int8.h"
#include "utils/numeric.h"
#include "utils/pg_locale.h"
Include dependency graph for formatting.c:

Go to the source code of this file.

Data Structures

struct  KeySuffix
struct  KeyWord
struct  FormatNode
struct  NUMDesc
struct  DCHCacheEntry
struct  NUMCacheEntry
struct  TmFromChar
struct  TmToChar
struct  NUMProc

Defines

#define DCH_TYPE   1
#define NUM_TYPE   2
#define KeyWord_INDEX_SIZE   ('~' - ' ')
#define KeyWord_INDEX_FILTER(_c)   ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
#define DCH_MAX_ITEM_SIZ   9
#define NUM_MAX_ITEM_SIZ   8
#define MAXFLOATWIDTH   60
#define MAXDOUBLEWIDTH   500
#define NODE_TYPE_END   1
#define NODE_TYPE_ACTION   2
#define NODE_TYPE_CHAR   3
#define SUFFTYPE_PREFIX   1
#define SUFFTYPE_POSTFIX   2
#define CLOCK_24_HOUR   0
#define CLOCK_12_HOUR   1
#define ADJUST_YEAR(year, is_interval)   ((is_interval) ? (year) : ((year) <= 0 ? -((year) - 1) : (year)))
#define A_D_STR   "A.D."
#define a_d_STR   "a.d."
#define AD_STR   "AD"
#define ad_STR   "ad"
#define B_C_STR   "B.C."
#define b_c_STR   "b.c."
#define BC_STR   "BC"
#define bc_STR   "bc"
#define A_M_STR   "A.M."
#define a_m_STR   "a.m."
#define AM_STR   "AM"
#define am_STR   "am"
#define P_M_STR   "P.M."
#define p_m_STR   "p.m."
#define PM_STR   "PM"
#define pm_STR   "pm"
#define ONE_UPPER   1
#define ALL_UPPER   2
#define ALL_LOWER   3
#define FULL_SIZ   0
#define MAX_MONTH_LEN   9
#define MAX_MON_LEN   3
#define MAX_DAY_LEN   9
#define MAX_DY_LEN   3
#define MAX_RM_LEN   4
#define TH_UPPER   1
#define TH_LOWER   2
#define NUM_F_DECIMAL   (1 << 1)
#define NUM_F_LDECIMAL   (1 << 2)
#define NUM_F_ZERO   (1 << 3)
#define NUM_F_BLANK   (1 << 4)
#define NUM_F_FILLMODE   (1 << 5)
#define NUM_F_LSIGN   (1 << 6)
#define NUM_F_BRACKET   (1 << 7)
#define NUM_F_MINUS   (1 << 8)
#define NUM_F_PLUS   (1 << 9)
#define NUM_F_ROMAN   (1 << 10)
#define NUM_F_MULTI   (1 << 11)
#define NUM_F_PLUS_POST   (1 << 12)
#define NUM_F_MINUS_POST   (1 << 13)
#define NUM_F_EEEE   (1 << 14)
#define NUM_LSIGN_PRE   (-1)
#define NUM_LSIGN_POST   1
#define NUM_LSIGN_NONE   0
#define IS_DECIMAL(_f)   ((_f)->flag & NUM_F_DECIMAL)
#define IS_LDECIMAL(_f)   ((_f)->flag & NUM_F_LDECIMAL)
#define IS_ZERO(_f)   ((_f)->flag & NUM_F_ZERO)
#define IS_BLANK(_f)   ((_f)->flag & NUM_F_BLANK)
#define IS_FILLMODE(_f)   ((_f)->flag & NUM_F_FILLMODE)
#define IS_BRACKET(_f)   ((_f)->flag & NUM_F_BRACKET)
#define IS_MINUS(_f)   ((_f)->flag & NUM_F_MINUS)
#define IS_LSIGN(_f)   ((_f)->flag & NUM_F_LSIGN)
#define IS_PLUS(_f)   ((_f)->flag & NUM_F_PLUS)
#define IS_ROMAN(_f)   ((_f)->flag & NUM_F_ROMAN)
#define IS_MULTI(_f)   ((_f)->flag & NUM_F_MULTI)
#define IS_EEEE(_f)   ((_f)->flag & NUM_F_EEEE)
#define NUM_CACHE_SIZE   64
#define NUM_CACHE_FIELDS   16
#define DCH_CACHE_SIZE   128
#define DCH_CACHE_FIELDS   16
#define ZERO_tmfc(_X)   memset(_X, 0, sizeof(TmFromChar))
#define DEBUG_TMFC(_X)
#define DEBUG_TM(_X)
#define tmtcTm(_X)   (&(_X)->tm)
#define tmtcTzn(_X)   ((_X)->tzn)
#define tmtcFsec(_X)   ((_X)->fsec)
#define ZERO_tm(_X)
#define ZERO_tmtc(_X)
#define INVALID_FOR_INTERVAL
#define DCH_S_FM   0x01
#define DCH_S_TH   0x02
#define DCH_S_th   0x04
#define DCH_S_SP   0x08
#define DCH_S_TM   0x10
#define S_THth(_s)   ((((_s) & DCH_S_TH) || ((_s) & DCH_S_th)) ? 1 : 0)
#define S_TH(_s)   (((_s) & DCH_S_TH) ? 1 : 0)
#define S_th(_s)   (((_s) & DCH_S_th) ? 1 : 0)
#define S_TH_TYPE(_s)   (((_s) & DCH_S_TH) ? TH_UPPER : TH_LOWER)
#define S_FM(_s)   (((_s) & DCH_S_FM) ? 1 : 0)
#define S_SP(_s)   (((_s) & DCH_S_SP) ? 1 : 0)
#define S_TM(_s)   (((_s) & DCH_S_TM) ? 1 : 0)
#define SKIP_THth(_suf)   (S_THth(_suf) ? 2 : 0)
#define zeroize_NUM(_n)
#define OVERLOAD_TEST   (Np->inout_p >= Np->inout + plen)
#define AMOUNT_TEST(_s)   (plen-(Np->inout_p-Np->inout) >= _s)
#define IS_PREDEC_SPACE(_n)
#define NUM_TOCHAR_prepare
#define NUM_TOCHAR_finish

Typedefs

typedef struct FormatNode FormatNode
typedef struct TmToChar TmToChar
typedef struct NUMProc NUMProc

Enumerations

enum  FromCharDateMode { FROM_CHAR_DATE_NONE = 0, FROM_CHAR_DATE_GREGORIAN, FROM_CHAR_DATE_ISOWEEK }
enum  DCH_poz {
  DCH_A_D, DCH_A_M, DCH_AD, DCH_AM,
  DCH_B_C, DCH_BC, DCH_CC, DCH_DAY,
  DCH_DDD, DCH_DD, DCH_DY, DCH_Day,
  DCH_Dy, DCH_D, DCH_FX, DCH_HH24,
  DCH_HH12, DCH_HH, DCH_IDDD, DCH_ID,
  DCH_IW, DCH_IYYY, DCH_IYY, DCH_IY,
  DCH_I, DCH_J, DCH_MI, DCH_MM,
  DCH_MONTH, DCH_MON, DCH_MS, DCH_Month,
  DCH_Mon, DCH_P_M, DCH_PM, DCH_Q,
  DCH_RM, DCH_SSSS, DCH_SS, DCH_TZ,
  DCH_US, DCH_WW, DCH_W, DCH_Y_YYY,
  DCH_YYYY, DCH_YYY, DCH_YY, DCH_Y,
  DCH_a_d, DCH_a_m, DCH_ad, DCH_am,
  DCH_b_c, DCH_bc, DCH_cc, DCH_day,
  DCH_ddd, DCH_dd, DCH_dy, DCH_d,
  DCH_fx, DCH_hh24, DCH_hh12, DCH_hh,
  DCH_iddd, DCH_id, DCH_iw, DCH_iyyy,
  DCH_iyy, DCH_iy, DCH_i, DCH_j,
  DCH_mi, DCH_mm, DCH_month, DCH_mon,
  DCH_ms, DCH_p_m, DCH_pm, DCH_q,
  DCH_rm, DCH_ssss, DCH_ss, DCH_tz,
  DCH_us, DCH_ww, DCH_w, DCH_y_yyy,
  DCH_yyyy, DCH_yyy, DCH_yy, DCH_y,
  _DCH_last_
}
enum  NUM_poz {
  NUM_COMMA, NUM_DEC, NUM_0, NUM_9,
  NUM_B, NUM_C, NUM_D, NUM_E,
  NUM_FM, NUM_G, NUM_L, NUM_MI,
  NUM_PL, NUM_PR, NUM_RN, NUM_SG,
  NUM_SP, NUM_S, NUM_TH, NUM_V,
  NUM_b, NUM_c, NUM_d, NUM_e,
  NUM_fm, NUM_g, NUM_l, NUM_mi,
  NUM_pl, NUM_pr, NUM_rn, NUM_sg,
  NUM_sp, NUM_s, NUM_th, NUM_v,
  _NUM_last_
}

Functions

static const KeyWordindex_seq_search (char *str, const KeyWord *kw, const int *index)
static KeySuffixsuff_search (char *str, KeySuffix *suf, int type)
static void NUMDesc_prepare (NUMDesc *num, FormatNode *n)
static void parse_format (FormatNode *node, char *str, const KeyWord *kw, KeySuffix *suf, const int *index, int ver, NUMDesc *Num)
static void DCH_to_char (FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid collid)
static void DCH_from_char (FormatNode *node, char *in, TmFromChar *out)
static char * get_th (char *num, int type)
static char * str_numth (char *dest, char *num, int type)
static int adjust_partial_year_to_2020 (int year)
static int strspace_len (char *str)
static int strdigits_len (char *str)
static void from_char_set_mode (TmFromChar *tmfc, const FromCharDateMode mode)
static void from_char_set_int (int *dest, const int value, const FormatNode *node)
static int from_char_parse_int_len (int *dest, char **src, const int len, FormatNode *node)
static int from_char_parse_int (int *dest, char **src, FormatNode *node)
static int seq_search (char *name, char **array, int type, int max, int *len)
static int from_char_seq_search (int *dest, char **src, char **array, int type, int max, FormatNode *node)
static void do_to_timestamp (text *date_txt, text *fmt, struct pg_tm *tm, fsec_t *fsec)
static char * fill_str (char *str, int c, int max)
static FormatNodeNUM_cache (int len, NUMDesc *Num, text *pars_str, bool *shouldFree)
static char * int_to_roman (int number)
static void NUM_prepare_locale (NUMProc *Np)
static char * get_last_relevant_decnum (char *num)
static void NUM_numpart_from_char (NUMProc *Np, int id, int plen)
static void NUM_numpart_to_char (NUMProc *Np, int id)
static char * NUM_processor (FormatNode *node, NUMDesc *Num, char *inout, char *number, int plen, int sign, bool is_to_char, Oid collid)
static DCHCacheEntryDCH_cache_search (char *str)
static DCHCacheEntryDCH_cache_getnew (char *str)
static NUMCacheEntryNUM_cache_search (char *str)
static NUMCacheEntryNUM_cache_getnew (char *str)
static void NUM_cache_remove (NUMCacheEntry *ent)
char * str_tolower (const char *buff, size_t nbytes, Oid collid)
char * str_toupper (const char *buff, size_t nbytes, Oid collid)
char * str_initcap (const char *buff, size_t nbytes, Oid collid)
char * asc_tolower (const char *buff, size_t nbytes)
char * asc_toupper (const char *buff, size_t nbytes)
char * asc_initcap (const char *buff, size_t nbytes)
static char * str_tolower_z (const char *buff, Oid collid)
static char * str_toupper_z (const char *buff, Oid collid)
static char * str_initcap_z (const char *buff, Oid collid)
static char * asc_tolower_z (const char *buff)
static char * asc_toupper_z (const char *buff)
static bool is_next_separator (FormatNode *n)
static textdatetime_to_char_body (TmToChar *tmtc, text *fmt, bool is_interval, Oid collid)
Datum timestamp_to_char (PG_FUNCTION_ARGS)
Datum timestamptz_to_char (PG_FUNCTION_ARGS)
Datum interval_to_char (PG_FUNCTION_ARGS)
Datum to_timestamp (PG_FUNCTION_ARGS)
Datum to_date (PG_FUNCTION_ARGS)
Datum numeric_to_number (PG_FUNCTION_ARGS)
Datum numeric_to_char (PG_FUNCTION_ARGS)
Datum int4_to_char (PG_FUNCTION_ARGS)
Datum int8_to_char (PG_FUNCTION_ARGS)
Datum float4_to_char (PG_FUNCTION_ARGS)
Datum float8_to_char (PG_FUNCTION_ARGS)

Variables

char * months []
char * days []
static char * months_full []
static char * days_short []
static char * adbc_strings [] = {ad_STR, bc_STR, AD_STR, BC_STR, NULL}
static char * adbc_strings_long [] = {a_d_STR, b_c_STR, A_D_STR, B_C_STR, NULL}
static char * ampm_strings [] = {am_STR, pm_STR, AM_STR, PM_STR, NULL}
static char * ampm_strings_long [] = {a_m_STR, p_m_STR, A_M_STR, P_M_STR, NULL}
static char * rm_months_upper []
static char * rm_months_lower []
static char * rm1 [] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", NULL}
static char * rm10 [] = {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", NULL}
static char * rm100 [] = {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", NULL}
static char * numTH [] = {"ST", "ND", "RD", "TH", NULL}
static char * numth [] = {"st", "nd", "rd", "th", NULL}
static DCHCacheEntry DCHCache [DCH_CACHE_FIELDS+1]
static int n_DCHCache = 0
static int DCHCounter = 0
static NUMCacheEntry NUMCache [NUM_CACHE_FIELDS+1]
static int n_NUMCache = 0
static int NUMCounter = 0
static NUMCacheEntrylast_NUMCacheEntry = NUMCache + 0
static KeySuffix DCH_suff []
static const KeyWord DCH_keywords []
static const KeyWord NUM_keywords []
static const int DCH_index [KeyWord_INDEX_SIZE]
static const int NUM_index [KeyWord_INDEX_SIZE]

Define Documentation

#define A_D_STR   "A.D."

Definition at line 209 of file formatting.c.

Referenced by DCH_to_char().

#define a_d_STR   "a.d."

Definition at line 210 of file formatting.c.

Referenced by DCH_to_char().

#define A_M_STR   "A.M."

Definition at line 236 of file formatting.c.

Referenced by DCH_to_char().

#define a_m_STR   "a.m."

Definition at line 237 of file formatting.c.

Referenced by DCH_to_char().

#define AD_STR   "AD"

Definition at line 211 of file formatting.c.

Referenced by DCH_to_char().

#define ad_STR   "ad"

Definition at line 212 of file formatting.c.

Referenced by DCH_to_char().

#define ADJUST_YEAR (   year,
  is_interval 
)    ((is_interval) ? (year) : ((year) <= 0 ? -((year) - 1) : (year)))

Definition at line 207 of file formatting.c.

Referenced by DCH_to_char().

#define ALL_LOWER   3

Definition at line 292 of file formatting.c.

Referenced by DCH_from_char(), and seq_search().

#define ALL_UPPER   2

Definition at line 291 of file formatting.c.

Referenced by DCH_from_char(), and seq_search().

#define AM_STR   "AM"

Definition at line 238 of file formatting.c.

Referenced by DCH_to_char().

#define am_STR   "am"

Definition at line 239 of file formatting.c.

Referenced by DCH_to_char().

#define AMOUNT_TEST (   _s  )     (plen-(Np->inout_p-Np->inout) >= _s)

Referenced by NUM_numpart_from_char().

#define B_C_STR   "B.C."

Definition at line 214 of file formatting.c.

Referenced by DCH_to_char().

#define b_c_STR   "b.c."

Definition at line 215 of file formatting.c.

Referenced by DCH_to_char().

#define BC_STR   "BC"

Definition at line 216 of file formatting.c.

Referenced by DCH_to_char().

#define bc_STR   "bc"

Definition at line 217 of file formatting.c.

Referenced by DCH_to_char().

#define CLOCK_12_HOUR   1

Definition at line 184 of file formatting.c.

Referenced by do_to_timestamp().

#define CLOCK_24_HOUR   0

Definition at line 183 of file formatting.c.

#define DCH_CACHE_FIELDS   16

Definition at line 373 of file formatting.c.

Referenced by DCH_cache_getnew(), and DCH_cache_search().

#define DCH_CACHE_SIZE   128

Definition at line 372 of file formatting.c.

Referenced by datetime_to_char_body(), DCH_cache_getnew(), and do_to_timestamp().

#define DCH_MAX_ITEM_SIZ   9
#define DCH_S_FM   0x01

Definition at line 504 of file formatting.c.

#define DCH_S_SP   0x08

Definition at line 507 of file formatting.c.

#define DCH_S_TH   0x02

Definition at line 505 of file formatting.c.

#define DCH_S_th   0x04

Definition at line 506 of file formatting.c.

#define DCH_S_TM   0x10

Definition at line 508 of file formatting.c.

#define DCH_TYPE   1

Definition at line 99 of file formatting.c.

Referenced by datetime_to_char_body(), do_to_timestamp(), and parse_format().

#define DEBUG_TM (   _X  ) 

Definition at line 451 of file formatting.c.

Referenced by do_to_timestamp().

#define DEBUG_TMFC (   _X  ) 

Definition at line 450 of file formatting.c.

Referenced by do_to_timestamp().

#define FULL_SIZ   0

Definition at line 294 of file formatting.c.

#define INVALID_FOR_INTERVAL
Value:
do { \
    if (is_interval) \
        ereport(ERROR, \
                (errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
                 errmsg("invalid format specification for an interval value"), \
                 errhint("Intervals are not tied to specific calendar dates."))); \
} while(0)

Definition at line 487 of file formatting.c.

#define IS_BLANK (   _f  )     ((_f)->flag & NUM_F_BLANK)

Definition at line 352 of file formatting.c.

Referenced by NUMDesc_prepare().

#define IS_BRACKET (   _f  )     ((_f)->flag & NUM_F_BRACKET)
#define IS_DECIMAL (   _f  )     ((_f)->flag & NUM_F_DECIMAL)
#define IS_EEEE (   _f  )     ((_f)->flag & NUM_F_EEEE)
#define IS_FILLMODE (   _f  )     ((_f)->flag & NUM_F_FILLMODE)

Definition at line 353 of file formatting.c.

Referenced by NUM_numpart_to_char(), NUM_processor(), and NUMDesc_prepare().

#define IS_LDECIMAL (   _f  )     ((_f)->flag & NUM_F_LDECIMAL)

Definition at line 350 of file formatting.c.

Referenced by NUM_prepare_locale().

#define IS_LSIGN (   _f  )     ((_f)->flag & NUM_F_LSIGN)
#define IS_MINUS (   _f  )     ((_f)->flag & NUM_F_MINUS)

Definition at line 355 of file formatting.c.

Referenced by NUM_numpart_from_char(), NUM_processor(), and NUMDesc_prepare().

#define IS_MULTI (   _f  )     ((_f)->flag & NUM_F_MULTI)
#define IS_PLUS (   _f  )     ((_f)->flag & NUM_F_PLUS)

Definition at line 357 of file formatting.c.

Referenced by NUM_numpart_from_char(), NUM_processor(), and NUMDesc_prepare().

#define IS_PREDEC_SPACE (   _n  ) 
Value:
(IS_ZERO((_n)->Num)==FALSE && \
         (_n)->number == (_n)->number_p && \
         *(_n)->number == '0' && \
                 (_n)->Num->post != 0)

Definition at line 4265 of file formatting.c.

Referenced by NUM_numpart_to_char().

#define IS_ROMAN (   _f  )     ((_f)->flag & NUM_F_ROMAN)
#define IS_ZERO (   _f  )     ((_f)->flag & NUM_F_ZERO)

Definition at line 351 of file formatting.c.

Referenced by NUM_numpart_to_char(), NUM_processor(), and NUMDesc_prepare().

#define KeyWord_INDEX_FILTER (   _c  )     ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)

Definition at line 107 of file formatting.c.

Referenced by index_seq_search().

#define KeyWord_INDEX_SIZE   ('~' - ' ')

Definition at line 106 of file formatting.c.

#define MAX_DAY_LEN   9

Definition at line 298 of file formatting.c.

Referenced by DCH_from_char().

#define MAX_DY_LEN   3

Definition at line 299 of file formatting.c.

Referenced by DCH_from_char().

#define MAX_MON_LEN   3

Definition at line 297 of file formatting.c.

Referenced by DCH_from_char().

#define MAX_MONTH_LEN   9

Definition at line 296 of file formatting.c.

Referenced by DCH_from_char().

#define MAX_RM_LEN   4

Definition at line 300 of file formatting.c.

Referenced by DCH_from_char().

#define MAXDOUBLEWIDTH   500

Definition at line 121 of file formatting.c.

Referenced by float4_to_char(), float8_to_char(), and int4_to_char().

#define MAXFLOATWIDTH   60

Definition at line 120 of file formatting.c.

Referenced by float4_to_char().

#define NODE_TYPE_ACTION   2
#define NODE_TYPE_CHAR   3

Definition at line 178 of file formatting.c.

#define NODE_TYPE_END   1
#define NUM_CACHE_FIELDS   16

Definition at line 371 of file formatting.c.

Referenced by NUM_cache_getnew(), and NUM_cache_search().

#define NUM_CACHE_SIZE   64

Definition at line 370 of file formatting.c.

Referenced by NUM_cache(), and NUM_cache_getnew().

#define NUM_F_BLANK   (1 << 4)

Definition at line 329 of file formatting.c.

#define NUM_F_BRACKET   (1 << 7)

Definition at line 332 of file formatting.c.

#define NUM_F_DECIMAL   (1 << 1)

Definition at line 326 of file formatting.c.

#define NUM_F_EEEE   (1 << 14)

Definition at line 339 of file formatting.c.

#define NUM_F_FILLMODE   (1 << 5)

Definition at line 330 of file formatting.c.

#define NUM_F_LDECIMAL   (1 << 2)

Definition at line 327 of file formatting.c.

#define NUM_F_LSIGN   (1 << 6)

Definition at line 331 of file formatting.c.

#define NUM_F_MINUS   (1 << 8)

Definition at line 333 of file formatting.c.

#define NUM_F_MINUS_POST   (1 << 13)

Definition at line 338 of file formatting.c.

#define NUM_F_MULTI   (1 << 11)

Definition at line 336 of file formatting.c.

#define NUM_F_PLUS   (1 << 9)

Definition at line 334 of file formatting.c.

#define NUM_F_PLUS_POST   (1 << 12)

Definition at line 337 of file formatting.c.

#define NUM_F_ROMAN   (1 << 10)

Definition at line 335 of file formatting.c.

#define NUM_F_ZERO   (1 << 3)

Definition at line 328 of file formatting.c.

#define NUM_LSIGN_NONE   0

Definition at line 343 of file formatting.c.

Referenced by NUMDesc_prepare().

#define NUM_LSIGN_POST   1

Definition at line 342 of file formatting.c.

#define NUM_LSIGN_PRE   (-1)

Definition at line 341 of file formatting.c.

Referenced by NUM_numpart_from_char(), NUM_numpart_to_char(), and NUM_processor().

#define NUM_MAX_ITEM_SIZ   8

Definition at line 114 of file formatting.c.

Referenced by numeric_to_number().

#define NUM_TOCHAR_finish
Value:
do { \
    NUM_processor(format, &Num, VARDATA(result), numstr, plen, sign, true, PG_GET_COLLATION()); \
                                    \
    if (shouldFree)                 \
        pfree(format);              \
                                    \
    /*                              \
     * Convert null-terminated representation of result to standard text. \
     * The result is usually much bigger than it needs to be, but there \
     * seems little point in realloc'ing it smaller. \
     */                             \
    len = strlen(VARDATA(result));  \
    SET_VARSIZE(result, len + VARHDRSZ); \
} while (0)

Definition at line 4891 of file formatting.c.

#define NUM_TOCHAR_prepare
Value:
do { \
    len = VARSIZE_ANY_EXHDR(fmt); \
    if (len <= 0 || len >= (INT_MAX-VARHDRSZ)/NUM_MAX_ITEM_SIZ)     \
        PG_RETURN_TEXT_P(cstring_to_text("")); \
    result  = (text *) palloc0((len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ);    \
    format  = NUM_cache(len, &Num, fmt, &shouldFree);       \
} while (0)

Definition at line 4878 of file formatting.c.

#define NUM_TYPE   2

Definition at line 100 of file formatting.c.

Referenced by NUM_cache(), and parse_format().

#define ONE_UPPER   1

Definition at line 290 of file formatting.c.

Referenced by DCH_from_char(), and seq_search().

#define OVERLOAD_TEST   (Np->inout_p >= Np->inout + plen)

Referenced by NUM_numpart_from_char().

#define P_M_STR   "P.M."

Definition at line 241 of file formatting.c.

Referenced by DCH_to_char().

#define p_m_STR   "p.m."

Definition at line 242 of file formatting.c.

Referenced by DCH_to_char().

#define PM_STR   "PM"

Definition at line 243 of file formatting.c.

Referenced by DCH_to_char().

#define pm_STR   "pm"

Definition at line 244 of file formatting.c.

Referenced by DCH_to_char().

#define S_FM (   _s  )     (((_s) & DCH_S_FM) ? 1 : 0)

Definition at line 520 of file formatting.c.

Referenced by DCH_to_char(), and from_char_parse_int_len().

#define S_SP (   _s  )     (((_s) & DCH_S_SP) ? 1 : 0)

Definition at line 521 of file formatting.c.

#define S_TH (   _s  )     (((_s) & DCH_S_TH) ? 1 : 0)

Definition at line 515 of file formatting.c.

#define S_th (   _s  )     (((_s) & DCH_S_th) ? 1 : 0)

Definition at line 516 of file formatting.c.

#define S_TH_TYPE (   _s  )     (((_s) & DCH_S_TH) ? TH_UPPER : TH_LOWER)

Definition at line 517 of file formatting.c.

Referenced by DCH_to_char().

#define S_THth (   _s  )     ((((_s) & DCH_S_TH) || ((_s) & DCH_S_th)) ? 1 : 0)

Definition at line 514 of file formatting.c.

Referenced by DCH_to_char(), and is_next_separator().

#define S_TM (   _s  )     (((_s) & DCH_S_TM) ? 1 : 0)

Definition at line 522 of file formatting.c.

Referenced by DCH_to_char().

#define SKIP_THth (   _suf  )     (S_THth(_suf) ? 2 : 0)

Definition at line 1983 of file formatting.c.

Referenced by DCH_from_char().

#define SUFFTYPE_POSTFIX   2

Definition at line 181 of file formatting.c.

Referenced by parse_format().

#define SUFFTYPE_PREFIX   1

Definition at line 180 of file formatting.c.

Referenced by parse_format().

#define TH_LOWER   2

Definition at line 303 of file formatting.c.

Referenced by NUM_processor().

#define TH_UPPER   1

Definition at line 302 of file formatting.c.

Referenced by get_th(), and NUM_processor().

#define tmtcFsec (   _X  )     ((_X)->fsec)

Definition at line 467 of file formatting.c.

Referenced by interval_to_char(), timestamp_to_char(), and timestamptz_to_char().

#define tmtcTm (   _X  )     (&(_X)->tm)

Definition at line 465 of file formatting.c.

Referenced by interval_to_char(), timestamp_to_char(), and timestamptz_to_char().

#define tmtcTzn (   _X  )     ((_X)->tzn)

Definition at line 466 of file formatting.c.

Referenced by DCH_to_char(), and timestamptz_to_char().

#define ZERO_tm (   _X  ) 
Value:
do {    \
    (_X)->tm_sec  = (_X)->tm_year = (_X)->tm_min = (_X)->tm_wday = \
    (_X)->tm_hour = (_X)->tm_yday = (_X)->tm_isdst = 0; \
    (_X)->tm_mday = (_X)->tm_mon  = 1; \
} while(0)

Definition at line 469 of file formatting.c.

Referenced by do_to_timestamp().

#define ZERO_tmfc (   _X  )     memset(_X, 0, sizeof(TmFromChar))

Definition at line 431 of file formatting.c.

Referenced by do_to_timestamp().

#define ZERO_tmtc (   _X  ) 
Value:
do { \
    ZERO_tm( tmtcTm(_X) ); \
    tmtcFsec(_X) = 0; \
    tmtcTzn(_X) = NULL; \
} while(0)

Definition at line 476 of file formatting.c.

Referenced by interval_to_char(), timestamp_to_char(), and timestamptz_to_char().

#define zeroize_NUM (   _n  ) 
Value:
do { \
    (_n)->flag      = 0;    \
    (_n)->lsign     = 0;    \
    (_n)->pre       = 0;    \
    (_n)->post      = 0;    \
    (_n)->pre_lsign_num = 0;    \
    (_n)->need_locale   = 0;    \
    (_n)->multi     = 0;    \
    (_n)->zero_start    = 0;    \
    (_n)->zero_end      = 0;    \
} while(0)

Definition at line 3692 of file formatting.c.

Referenced by NUM_cache(), and NUM_cache_getnew().


Typedef Documentation

typedef struct FormatNode FormatNode

Definition at line 157 of file formatting.c.

typedef struct NUMProc NUMProc
typedef struct TmToChar TmToChar

Enumeration Type Documentation

enum DCH_poz
Enumerator:
DCH_A_D 
DCH_A_M 
DCH_AD 
DCH_AM 
DCH_B_C 
DCH_BC 
DCH_CC 
DCH_DAY 
DCH_DDD 
DCH_DD 
DCH_DY 
DCH_Day 
DCH_Dy 
DCH_D 
DCH_FX 
DCH_HH24 
DCH_HH12 
DCH_HH 
DCH_IDDD 
DCH_ID 
DCH_IW 
DCH_IYYY 
DCH_IYY 
DCH_IY 
DCH_I 
DCH_J 
DCH_MI 
DCH_MM 
DCH_MONTH 
DCH_MON 
DCH_MS 
DCH_Month 
DCH_Mon 
DCH_P_M 
DCH_PM 
DCH_Q 
DCH_RM 
DCH_SSSS 
DCH_SS 
DCH_TZ 
DCH_US 
DCH_WW 
DCH_W 
DCH_Y_YYY 
DCH_YYYY 
DCH_YYY 
DCH_YY 
DCH_Y 
DCH_a_d 
DCH_a_m 
DCH_ad 
DCH_am 
DCH_b_c 
DCH_bc 
DCH_cc 
DCH_day 
DCH_ddd 
DCH_dd 
DCH_dy 
DCH_d 
DCH_fx 
DCH_hh24 
DCH_hh12 
DCH_hh 
DCH_iddd 
DCH_id 
DCH_iw 
DCH_iyyy 
DCH_iyy 
DCH_iy 
DCH_i 
DCH_j 
DCH_mi 
DCH_mm 
DCH_month 
DCH_mon 
DCH_ms 
DCH_p_m 
DCH_pm 
DCH_q 
DCH_rm 
DCH_ssss 
DCH_ss 
DCH_tz 
DCH_us 
DCH_ww 
DCH_w 
DCH_y_yyy 
DCH_yyyy 
DCH_yyy 
DCH_yy 
DCH_y 
_DCH_last_ 

Definition at line 568 of file formatting.c.

{
    DCH_A_D,
    DCH_A_M,
    DCH_AD,
    DCH_AM,
    DCH_B_C,
    DCH_BC,
    DCH_CC,
    DCH_DAY,
    DCH_DDD,
    DCH_DD,
    DCH_DY,
    DCH_Day,
    DCH_Dy,
    DCH_D,
    DCH_FX,                     /* global suffix */
    DCH_HH24,
    DCH_HH12,
    DCH_HH,
    DCH_IDDD,
    DCH_ID,
    DCH_IW,
    DCH_IYYY,
    DCH_IYY,
    DCH_IY,
    DCH_I,
    DCH_J,
    DCH_MI,
    DCH_MM,
    DCH_MONTH,
    DCH_MON,
    DCH_MS,
    DCH_Month,
    DCH_Mon,
    DCH_P_M,
    DCH_PM,
    DCH_Q,
    DCH_RM,
    DCH_SSSS,
    DCH_SS,
    DCH_TZ,
    DCH_US,
    DCH_WW,
    DCH_W,
    DCH_Y_YYY,
    DCH_YYYY,
    DCH_YYY,
    DCH_YY,
    DCH_Y,
    DCH_a_d,
    DCH_a_m,
    DCH_ad,
    DCH_am,
    DCH_b_c,
    DCH_bc,
    DCH_cc,
    DCH_day,
    DCH_ddd,
    DCH_dd,
    DCH_dy,
    DCH_d,
    DCH_fx,
    DCH_hh24,
    DCH_hh12,
    DCH_hh,
    DCH_iddd,
    DCH_id,
    DCH_iw,
    DCH_iyyy,
    DCH_iyy,
    DCH_iy,
    DCH_i,
    DCH_j,
    DCH_mi,
    DCH_mm,
    DCH_month,
    DCH_mon,
    DCH_ms,
    DCH_p_m,
    DCH_pm,
    DCH_q,
    DCH_rm,
    DCH_ssss,
    DCH_ss,
    DCH_tz,
    DCH_us,
    DCH_ww,
    DCH_w,
    DCH_y_yyy,
    DCH_yyyy,
    DCH_yyy,
    DCH_yy,
    DCH_y,

    /* last */
    _DCH_last_
}   DCH_poz;

Enumerator:
FROM_CHAR_DATE_NONE 
FROM_CHAR_DATE_GREGORIAN 
FROM_CHAR_DATE_ISOWEEK 

Definition at line 150 of file formatting.c.

{
    FROM_CHAR_DATE_NONE = 0,    /* Value does not affect date mode. */
    FROM_CHAR_DATE_GREGORIAN,   /* Gregorian (day, month, year) style date */
    FROM_CHAR_DATE_ISOWEEK      /* ISO 8601 week date */
} FromCharDateMode;

enum NUM_poz
Enumerator:
NUM_COMMA 
NUM_DEC 
NUM_0 
NUM_9 
NUM_B 
NUM_C 
NUM_D 
NUM_E 
NUM_FM 
NUM_G 
NUM_L 
NUM_MI 
NUM_PL 
NUM_PR 
NUM_RN 
NUM_SG 
NUM_SP 
NUM_S 
NUM_TH 
NUM_V 
NUM_b 
NUM_c 
NUM_d 
NUM_e 
NUM_fm 
NUM_g 
NUM_l 
NUM_mi 
NUM_pl 
NUM_pr 
NUM_rn 
NUM_sg 
NUM_sp 
NUM_s 
NUM_th 
NUM_v 
_NUM_last_ 

Definition at line 667 of file formatting.c.

{
    NUM_COMMA,
    NUM_DEC,
    NUM_0,
    NUM_9,
    NUM_B,
    NUM_C,
    NUM_D,
    NUM_E,
    NUM_FM,
    NUM_G,
    NUM_L,
    NUM_MI,
    NUM_PL,
    NUM_PR,
    NUM_RN,
    NUM_SG,
    NUM_SP,
    NUM_S,
    NUM_TH,
    NUM_V,
    NUM_b,
    NUM_c,
    NUM_d,
    NUM_e,
    NUM_fm,
    NUM_g,
    NUM_l,
    NUM_mi,
    NUM_pl,
    NUM_pr,
    NUM_rn,
    NUM_sg,
    NUM_sp,
    NUM_s,
    NUM_th,
    NUM_v,

    /* last */
    _NUM_last_
}   NUM_poz;


Function Documentation

static int adjust_partial_year_to_2020 ( int  year  )  [static]

Definition at line 2055 of file formatting.c.

Referenced by DCH_from_char().

{
    /*
     * Adjust all dates toward 2020; this is effectively what happens when we
     * assume '70' is 1970 and '69' is 2069.
     */
    /* Force 0-69 into the 2000's */
    if (year < 70)
        return year + 2000;
    /* Force 70-99 into the 1900's */
    else if (year < 100)
        return year + 1900;
    /* Force 100-519 into the 2000's */
    else if (year < 520)
        return year + 2000;
    /* Force 520-999 into the 1000's */
    else if (year < 1000)
        return year + 1000;
    else
        return year;
}

char* asc_initcap ( const char *  buff,
size_t  nbytes 
)

Definition at line 1916 of file formatting.c.

References pg_ascii_tolower(), pg_ascii_toupper(), and pnstrdup().

Referenced by str_initcap().

{
    char       *result;
    char       *p;
    int         wasalnum = false;

    if (!buff)
        return NULL;

    result = pnstrdup(buff, nbytes);

    for (p = result; *p; p++)
    {
        char        c;

        if (wasalnum)
            *p = c = pg_ascii_tolower((unsigned char) *p);
        else
            *p = c = pg_ascii_toupper((unsigned char) *p);
        /* we don't trust isalnum() here */
        wasalnum = ((c >= 'A' && c <= 'Z') ||
                    (c >= 'a' && c <= 'z') ||
                    (c >= '0' && c <= '9'));
    }

    return result;
}

char* asc_tolower ( const char *  buff,
size_t  nbytes 
)

Definition at line 1870 of file formatting.c.

References pg_ascii_tolower(), and pnstrdup().

Referenced by asc_tolower_z(), and str_tolower().

{
    char       *result;
    char       *p;

    if (!buff)
        return NULL;

    result = pnstrdup(buff, nbytes);

    for (p = result; *p; p++)
        *p = pg_ascii_tolower((unsigned char) *p);

    return result;
}

static char* asc_tolower_z ( const char *  buff  )  [static]

Definition at line 1965 of file formatting.c.

References asc_tolower().

Referenced by DCH_to_char(), and NUM_processor().

{
    return asc_tolower(buff, strlen(buff));
}

char* asc_toupper ( const char *  buff,
size_t  nbytes 
)

Definition at line 1893 of file formatting.c.

References pg_ascii_toupper(), and pnstrdup().

Referenced by asc_toupper_z(), and str_toupper().

{
    char       *result;
    char       *p;

    if (!buff)
        return NULL;

    result = pnstrdup(buff, nbytes);

    for (p = result; *p; p++)
        *p = pg_ascii_toupper((unsigned char) *p);

    return result;
}

static char* asc_toupper_z ( const char *  buff  )  [static]

Definition at line 1971 of file formatting.c.

References asc_toupper().

Referenced by DCH_to_char().

{
    return asc_toupper(buff, strlen(buff));
}

static text* datetime_to_char_body ( TmToChar tmtc,
text fmt,
bool  is_interval,
Oid  collid 
) [static]

Definition at line 3169 of file formatting.c.

References cstring_to_text(), DCH_cache_getnew(), DCH_cache_search(), DCH_CACHE_SIZE, DCH_index, DCH_MAX_ITEM_SIZ, DCH_to_char(), DCH_TYPE, DCHCacheEntry::format, format, NODE_TYPE_END, NULL, palloc(), parse_format(), pfree(), and text_to_cstring().

Referenced by interval_to_char(), timestamp_to_char(), and timestamptz_to_char().

{
    FormatNode *format;
    char       *fmt_str,
               *result;
    bool        incache;
    int         fmt_len;
    text       *res;

    /*
     * Convert fmt to C string
     */
    fmt_str = text_to_cstring(fmt);
    fmt_len = strlen(fmt_str);

    /*
     * Allocate workspace for result as C string
     */
    result = palloc((fmt_len * DCH_MAX_ITEM_SIZ) + 1);
    *result = '\0';

    /*
     * Allocate new memory if format picture is bigger than static cache and
     * not use cache (call parser always)
     */
    if (fmt_len > DCH_CACHE_SIZE)
    {
        format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
        incache = FALSE;

        parse_format(format, fmt_str, DCH_keywords,
                     DCH_suff, DCH_index, DCH_TYPE, NULL);

        (format + fmt_len)->type = NODE_TYPE_END;       /* Paranoia? */
    }
    else
    {
        /*
         * Use cache buffers
         */
        DCHCacheEntry *ent;

        incache = TRUE;

        if ((ent = DCH_cache_search(fmt_str)) == NULL)
        {
            ent = DCH_cache_getnew(fmt_str);

            /*
             * Not in the cache, must run parser and save a new format-picture
             * to the cache.
             */
            parse_format(ent->format, fmt_str, DCH_keywords,
                         DCH_suff, DCH_index, DCH_TYPE, NULL);

            (ent->format + fmt_len)->type = NODE_TYPE_END;      /* Paranoia? */

#ifdef DEBUG_TO_FROM_CHAR
            /* dump_node(ent->format, fmt_len); */
            /* dump_index(DCH_keywords, DCH_index);  */
#endif
        }
        format = ent->format;
    }

    /* The real work is here */
    DCH_to_char(format, is_interval, tmtc, result, collid);

    if (!incache)
        pfree(format);

    pfree(fmt_str);

    /* convert C-string result to TEXT format */
    res = cstring_to_text(result);

    pfree(result);
    return res;
}

static DCHCacheEntry * DCH_cache_getnew ( char *  str  )  [static]

Definition at line 3086 of file formatting.c.

References DCHCacheEntry::age, DCH_CACHE_FIELDS, DCH_CACHE_SIZE, DCHCounter, elog, n_DCHCache, DCHCacheEntry::str, and StrNCpy.

Referenced by datetime_to_char_body(), and do_to_timestamp().

{
    DCHCacheEntry *ent;

    /* counter overflow check - paranoia? */
    if (DCHCounter >= (INT_MAX - DCH_CACHE_FIELDS - 1))
    {
        DCHCounter = 0;

        for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
            ent->age = (++DCHCounter);
    }

    /*
     * If cache is full, remove oldest entry
     */
    if (n_DCHCache > DCH_CACHE_FIELDS)
    {
        DCHCacheEntry *old = DCHCache + 0;

#ifdef DEBUG_TO_FROM_CHAR
        elog(DEBUG_elog_output, "cache is full (%d)", n_DCHCache);
#endif
        for (ent = DCHCache + 1; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
        {
            if (ent->age < old->age)
                old = ent;
        }
#ifdef DEBUG_TO_FROM_CHAR
        elog(DEBUG_elog_output, "OLD: '%s' AGE: %d", old->str, old->age);
#endif
        StrNCpy(old->str, str, DCH_CACHE_SIZE + 1);
        /* old->format fill parser */
        old->age = (++DCHCounter);
        return old;
    }
    else
    {
#ifdef DEBUG_TO_FROM_CHAR
        elog(DEBUG_elog_output, "NEW (%d)", n_DCHCache);
#endif
        ent = DCHCache + n_DCHCache;
        StrNCpy(ent->str, str, DCH_CACHE_SIZE + 1);
        /* ent->format fill parser */
        ent->age = (++DCHCounter);
        ++n_DCHCache;
        return ent;
    }
}

static DCHCacheEntry * DCH_cache_search ( char *  str  )  [static]

Definition at line 3137 of file formatting.c.

References DCHCacheEntry::age, DCH_CACHE_FIELDS, DCHCounter, i, n_DCHCache, and DCHCacheEntry::str.

Referenced by datetime_to_char_body(), and do_to_timestamp().

{
    int         i;
    DCHCacheEntry *ent;

    /* counter overflow check - paranoia? */
    if (DCHCounter >= (INT_MAX - DCH_CACHE_FIELDS - 1))
    {
        DCHCounter = 0;

        for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
            ent->age = (++DCHCounter);
    }

    for (i = 0, ent = DCHCache; i < n_DCHCache; i++, ent++)
    {
        if (strcmp(ent->str, str) == 0)
        {
            ent->age = (++DCHCounter);
            return ent;
        }
    }

    return NULL;
}

static void DCH_from_char ( FormatNode node,
char *  in,
TmFromChar out 
) [static]

Definition at line 2826 of file formatting.c.

References adbc_strings, adbc_strings_long, adjust_partial_year_to_2020(), ALL_LOWER, ALL_UPPER, ampm_strings, ampm_strings_long, TmFromChar::bc, TmFromChar::cc, FormatNode::character, TmFromChar::clock, TmFromChar::d, KeyWord::date_mode, days, DCH_a_d, DCH_A_D, DCH_a_m, DCH_A_M, DCH_ad, DCH_AD, DCH_am, DCH_AM, DCH_b_c, DCH_B_C, DCH_bc, DCH_BC, DCH_CC, DCH_D, DCH_day, DCH_Day, DCH_DAY, DCH_DD, DCH_DDD, DCH_dy, DCH_Dy, DCH_DY, DCH_FX, DCH_HH, DCH_HH12, DCH_HH24, DCH_I, DCH_ID, DCH_IDDD, DCH_IW, DCH_IY, DCH_IYY, DCH_IYYY, DCH_J, DCH_MI, DCH_MM, DCH_mon, DCH_Mon, DCH_MON, DCH_month, DCH_Month, DCH_MONTH, DCH_MS, DCH_p_m, DCH_P_M, DCH_pm, DCH_PM, DCH_Q, DCH_rm, DCH_RM, DCH_SS, DCH_SSSS, DCH_TZ, DCH_tz, DCH_US, DCH_W, DCH_WW, DCH_Y, DCH_Y_YYY, DCH_YY, DCH_YYY, DCH_YYYY, TmFromChar::dd, TmFromChar::ddd, ereport, errcode(), errmsg(), ERROR, from_char_parse_int(), from_char_parse_int_len(), from_char_seq_search(), from_char_set_int(), from_char_set_mode(), TmFromChar::hh, KeyWord::id, TmFromChar::j, FormatNode::key, KeyWord::len, MAX_DAY_LEN, MAX_DY_LEN, MAX_MON_LEN, MAX_MONTH_LEN, MAX_RM_LEN, TmFromChar::mi, TmFromChar::mm, months, months_full, MONTHS_PER_YEAR, TmFromChar::ms, NODE_TYPE_ACTION, NODE_TYPE_END, NULL, ONE_UPPER, TmFromChar::pm, rm_months_lower, rm_months_upper, SKIP_THth, TmFromChar::ss, TmFromChar::ssss, strdigits_len(), FormatNode::suffix, FormatNode::type, TmFromChar::us, value, TmFromChar::w, TmFromChar::ww, TmFromChar::year, and TmFromChar::yysz.

Referenced by do_to_timestamp().

{
    FormatNode *n;
    char       *s;
    int         len,
                value;
    bool        fx_mode = false;

    for (n = node, s = in; n->type != NODE_TYPE_END && *s != '\0'; n++)
    {
        if (n->type != NODE_TYPE_ACTION)
        {
            s++;
            /* Ignore spaces when not in FX (fixed width) mode */
            if (isspace((unsigned char) n->character) && !fx_mode)
            {
                while (*s != '\0' && isspace((unsigned char) *s))
                    s++;
            }
            continue;
        }

        from_char_set_mode(out, n->key->date_mode);

        switch (n->key->id)
        {
            case DCH_FX:
                fx_mode = true;
                break;
            case DCH_A_M:
            case DCH_P_M:
            case DCH_a_m:
            case DCH_p_m:
                from_char_seq_search(&value, &s, ampm_strings_long,
                                     ALL_UPPER, n->key->len, n);
                from_char_set_int(&out->pm, value % 2, n);
                out->clock = CLOCK_12_HOUR;
                break;
            case DCH_AM:
            case DCH_PM:
            case DCH_am:
            case DCH_pm:
                from_char_seq_search(&value, &s, ampm_strings,
                                     ALL_UPPER, n->key->len, n);
                from_char_set_int(&out->pm, value % 2, n);
                out->clock = CLOCK_12_HOUR;
                break;
            case DCH_HH:
            case DCH_HH12:
                from_char_parse_int_len(&out->hh, &s, 2, n);
                out->clock = CLOCK_12_HOUR;
                s += SKIP_THth(n->suffix);
                break;
            case DCH_HH24:
                from_char_parse_int_len(&out->hh, &s, 2, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_MI:
                from_char_parse_int(&out->mi, &s, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_SS:
                from_char_parse_int(&out->ss, &s, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_MS:        /* millisecond */
                len = from_char_parse_int_len(&out->ms, &s, 3, n);

                /*
                 * 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25
                 */
                out->ms *= len == 1 ? 100 :
                    len == 2 ? 10 : 1;

                s += SKIP_THth(n->suffix);
                break;
            case DCH_US:        /* microsecond */
                len = from_char_parse_int_len(&out->us, &s, 6, n);

                out->us *= len == 1 ? 100000 :
                    len == 2 ? 10000 :
                    len == 3 ? 1000 :
                    len == 4 ? 100 :
                    len == 5 ? 10 : 1;

                s += SKIP_THth(n->suffix);
                break;
            case DCH_SSSS:
                from_char_parse_int(&out->ssss, &s, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_tz:
            case DCH_TZ:
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                         errmsg("\"TZ\"/\"tz\" format patterns are not supported in to_date")));
            case DCH_A_D:
            case DCH_B_C:
            case DCH_a_d:
            case DCH_b_c:
                from_char_seq_search(&value, &s, adbc_strings_long,
                                     ALL_UPPER, n->key->len, n);
                from_char_set_int(&out->bc, value % 2, n);
                break;
            case DCH_AD:
            case DCH_BC:
            case DCH_ad:
            case DCH_bc:
                from_char_seq_search(&value, &s, adbc_strings,
                                     ALL_UPPER, n->key->len, n);
                from_char_set_int(&out->bc, value % 2, n);
                break;
            case DCH_MONTH:
            case DCH_Month:
            case DCH_month:
                from_char_seq_search(&value, &s, months_full, ONE_UPPER,
                                     MAX_MONTH_LEN, n);
                from_char_set_int(&out->mm, value + 1, n);
                break;
            case DCH_MON:
            case DCH_Mon:
            case DCH_mon:
                from_char_seq_search(&value, &s, months, ONE_UPPER,
                                     MAX_MON_LEN, n);
                from_char_set_int(&out->mm, value + 1, n);
                break;
            case DCH_MM:
                from_char_parse_int(&out->mm, &s, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_DAY:
            case DCH_Day:
            case DCH_day:
                from_char_seq_search(&value, &s, days, ONE_UPPER,
                                     MAX_DAY_LEN, n);
                from_char_set_int(&out->d, value, n);
                out->d++;
                break;
            case DCH_DY:
            case DCH_Dy:
            case DCH_dy:
                from_char_seq_search(&value, &s, days, ONE_UPPER,
                                     MAX_DY_LEN, n);
                from_char_set_int(&out->d, value, n);
                out->d++;
                break;
            case DCH_DDD:
                from_char_parse_int(&out->ddd, &s, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_IDDD:
                from_char_parse_int_len(&out->ddd, &s, 3, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_DD:
                from_char_parse_int(&out->dd, &s, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_D:
                from_char_parse_int(&out->d, &s, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_ID:
                from_char_parse_int_len(&out->d, &s, 1, n);
                /* Shift numbering to match Gregorian where Sunday = 1 */
                if (++out->d > 7)
                    out->d = 1;
                s += SKIP_THth(n->suffix);
                break;
            case DCH_WW:
            case DCH_IW:
                from_char_parse_int(&out->ww, &s, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_Q:

                /*
                 * We ignore 'Q' when converting to date because it is unclear
                 * which date in the quarter to use, and some people specify
                 * both quarter and month, so if it was honored it might
                 * conflict with the supplied month. That is also why we don't
                 * throw an error.
                 *
                 * We still parse the source string for an integer, but it
                 * isn't stored anywhere in 'out'.
                 */
                from_char_parse_int((int *) NULL, &s, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_CC:
                from_char_parse_int(&out->cc, &s, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_Y_YYY:
                {
                    int         matched,
                                years,
                                millenia;

                    matched = sscanf(s, "%d,%03d", &millenia, &years);
                    if (matched != 2)
                        ereport(ERROR,
                                (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                              errmsg("invalid input string for \"Y,YYY\"")));
                    years += (millenia * 1000);
                    from_char_set_int(&out->year, years, n);
                    out->yysz = 4;
                    s += strdigits_len(s) + 4 + SKIP_THth(n->suffix);
                }
                break;
            case DCH_YYYY:
            case DCH_IYYY:
                from_char_parse_int(&out->year, &s, n);
                out->yysz = 4;
                s += SKIP_THth(n->suffix);
                break;
            case DCH_YYY:
            case DCH_IYY:
                if (from_char_parse_int(&out->year, &s, n) < 4)
                    out->year = adjust_partial_year_to_2020(out->year);
                out->yysz = 3;
                s += SKIP_THth(n->suffix);
                break;
            case DCH_YY:
            case DCH_IY:
                if (from_char_parse_int(&out->year, &s, n) < 4)
                    out->year = adjust_partial_year_to_2020(out->year);
                out->yysz = 2;
                s += SKIP_THth(n->suffix);
                break;
            case DCH_Y:
            case DCH_I:
                if (from_char_parse_int(&out->year, &s, n) < 4)
                    out->year = adjust_partial_year_to_2020(out->year);
                out->yysz = 1;
                s += SKIP_THth(n->suffix);
                break;
            case DCH_RM:
                from_char_seq_search(&value, &s, rm_months_upper,
                                     ALL_UPPER, MAX_RM_LEN, n);
                from_char_set_int(&out->mm, MONTHS_PER_YEAR - value, n);
                break;
            case DCH_rm:
                from_char_seq_search(&value, &s, rm_months_lower,
                                     ALL_LOWER, MAX_RM_LEN, n);
                from_char_set_int(&out->mm, MONTHS_PER_YEAR - value, n);
                break;
            case DCH_W:
                from_char_parse_int(&out->w, &s, n);
                s += SKIP_THth(n->suffix);
                break;
            case DCH_J:
                from_char_parse_int(&out->j, &s, n);
                s += SKIP_THth(n->suffix);
                break;
        }
    }
}

static void DCH_to_char ( FormatNode node,
bool  is_interval,
TmToChar in,
char *  out,
Oid  collid 
) [static]

Definition at line 2378 of file formatting.c.

References a_d_STR, A_D_STR, a_m_STR, A_M_STR, ad_STR, AD_STR, ADJUST_YEAR, am_STR, AM_STR, asc_tolower_z(), asc_toupper_z(), b_c_STR, B_C_STR, bc_STR, BC_STR, cache_locale_time(), FormatNode::character, date2isoweek(), date2isoyear(), date2isoyearday(), date2j(), days, days_short, DCH_a_d, DCH_A_D, DCH_a_m, DCH_A_M, DCH_ad, DCH_AD, DCH_am, DCH_AM, DCH_b_c, DCH_B_C, DCH_bc, DCH_BC, DCH_CC, DCH_D, DCH_day, DCH_Day, DCH_DAY, DCH_DD, DCH_DDD, DCH_dy, DCH_Dy, DCH_DY, DCH_HH, DCH_HH12, DCH_HH24, DCH_I, DCH_ID, DCH_IDDD, DCH_IW, DCH_IY, DCH_IYY, DCH_IYYY, DCH_J, DCH_MI, DCH_MM, DCH_mon, DCH_Mon, DCH_MON, DCH_month, DCH_Month, DCH_MONTH, DCH_MS, DCH_p_m, DCH_P_M, DCH_pm, DCH_PM, DCH_Q, DCH_rm, DCH_RM, DCH_SS, DCH_SSSS, DCH_TZ, DCH_tz, DCH_US, DCH_W, DCH_WW, DCH_Y, DCH_Y_YYY, DCH_YY, DCH_YYY, DCH_YYYY, TmToChar::fsec, HOURS_PER_DAY, i, KeyWord::id, INT64CONST, FormatNode::key, localized_abbrev_days, localized_abbrev_months, localized_full_days, localized_full_months, months, months_full, MONTHS_PER_YEAR, NODE_TYPE_ACTION, p_m_STR, P_M_STR, pfree(), pm_STR, PM_STR, rm_months_lower, rm_months_upper, S_FM, S_TH_TYPE, S_THth, S_TM, SECS_PER_HOUR, SECS_PER_MINUTE, str_initcap_z(), str_numth(), str_tolower_z(), str_toupper_z(), FormatNode::suffix, TmToChar::tm, tm, pg_tm::tm_hour, pg_tm::tm_mday, pg_tm::tm_min, pg_tm::tm_mon, pg_tm::tm_sec, pg_tm::tm_wday, pg_tm::tm_yday, pg_tm::tm_year, tmtcTzn, and FormatNode::type.

Referenced by datetime_to_char_body().

{
    FormatNode *n;
    char       *s;
    struct pg_tm *tm = &in->tm;
    int         i;

    /* cache localized days and months */
    cache_locale_time();

    s = out;
    for (n = node; n->type != NODE_TYPE_END; n++)
    {
        if (n->type != NODE_TYPE_ACTION)
        {
            *s = n->character;
            s++;
            continue;
        }

        switch (n->key->id)
        {
            case DCH_A_M:
            case DCH_P_M:
                strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
                       ? P_M_STR : A_M_STR);
                s += strlen(s);
                break;
            case DCH_AM:
            case DCH_PM:
                strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
                       ? PM_STR : AM_STR);
                s += strlen(s);
                break;
            case DCH_a_m:
            case DCH_p_m:
                strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
                       ? p_m_STR : a_m_STR);
                s += strlen(s);
                break;
            case DCH_am:
            case DCH_pm:
                strcpy(s, (tm->tm_hour % HOURS_PER_DAY >= HOURS_PER_DAY / 2)
                       ? pm_STR : am_STR);
                s += strlen(s);
                break;
            case DCH_HH:
            case DCH_HH12:

                /*
                 * display time as shown on a 12-hour clock, even for
                 * intervals
                 */
                sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
                 tm->tm_hour % (HOURS_PER_DAY / 2) == 0 ? HOURS_PER_DAY / 2 :
                        tm->tm_hour % (HOURS_PER_DAY / 2));
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_HH24:
                sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_hour);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_MI:
                sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_min);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_SS:
                sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_sec);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_MS:        /* millisecond */
#ifdef HAVE_INT64_TIMESTAMP
                sprintf(s, "%03d", (int) (in->fsec / INT64CONST(1000)));
#else
                /* No rint() because we can't overflow and we might print US */
                sprintf(s, "%03d", (int) (in->fsec * 1000));
#endif
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_US:        /* microsecond */
#ifdef HAVE_INT64_TIMESTAMP
                sprintf(s, "%06d", (int) in->fsec);
#else
                /* don't use rint() because we can't overflow 1000 */
                sprintf(s, "%06d", (int) (in->fsec * 1000000));
#endif
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_SSSS:
                sprintf(s, "%d", tm->tm_hour * SECS_PER_HOUR +
                        tm->tm_min * SECS_PER_MINUTE +
                        tm->tm_sec);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_tz:
                INVALID_FOR_INTERVAL;
                if (tmtcTzn(in))
                {
                    /* We assume here that timezone names aren't localized */
                    char       *p = asc_tolower_z(tmtcTzn(in));

                    strcpy(s, p);
                    pfree(p);
                    s += strlen(s);
                }
                break;
            case DCH_TZ:
                INVALID_FOR_INTERVAL;
                if (tmtcTzn(in))
                {
                    strcpy(s, tmtcTzn(in));
                    s += strlen(s);
                }
                break;
            case DCH_A_D:
            case DCH_B_C:
                INVALID_FOR_INTERVAL;
                strcpy(s, (tm->tm_year <= 0 ? B_C_STR : A_D_STR));
                s += strlen(s);
                break;
            case DCH_AD:
            case DCH_BC:
                INVALID_FOR_INTERVAL;
                strcpy(s, (tm->tm_year <= 0 ? BC_STR : AD_STR));
                s += strlen(s);
                break;
            case DCH_a_d:
            case DCH_b_c:
                INVALID_FOR_INTERVAL;
                strcpy(s, (tm->tm_year <= 0 ? b_c_STR : a_d_STR));
                s += strlen(s);
                break;
            case DCH_ad:
            case DCH_bc:
                INVALID_FOR_INTERVAL;
                strcpy(s, (tm->tm_year <= 0 ? bc_STR : ad_STR));
                s += strlen(s);
                break;
            case DCH_MONTH:
                INVALID_FOR_INTERVAL;
                if (!tm->tm_mon)
                    break;
                if (S_TM(n->suffix))
                    strcpy(s, str_toupper_z(localized_full_months[tm->tm_mon - 1], collid));
                else
                    sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
                         asc_toupper_z(months_full[tm->tm_mon - 1]));
                s += strlen(s);
                break;
            case DCH_Month:
                INVALID_FOR_INTERVAL;
                if (!tm->tm_mon)
                    break;
                if (S_TM(n->suffix))
                    strcpy(s, str_initcap_z(localized_full_months[tm->tm_mon - 1], collid));
                else
                    sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
                            months_full[tm->tm_mon - 1]);
                s += strlen(s);
                break;
            case DCH_month:
                INVALID_FOR_INTERVAL;
                if (!tm->tm_mon)
                    break;
                if (S_TM(n->suffix))
                    strcpy(s, str_tolower_z(localized_full_months[tm->tm_mon - 1], collid));
                else
                    sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
                            asc_tolower_z(months_full[tm->tm_mon - 1]));
                s += strlen(s);
                break;
            case DCH_MON:
                INVALID_FOR_INTERVAL;
                if (!tm->tm_mon)
                    break;
                if (S_TM(n->suffix))
                    strcpy(s, str_toupper_z(localized_abbrev_months[tm->tm_mon - 1], collid));
                else
                    strcpy(s, asc_toupper_z(months[tm->tm_mon - 1]));
                s += strlen(s);
                break;
            case DCH_Mon:
                INVALID_FOR_INTERVAL;
                if (!tm->tm_mon)
                    break;
                if (S_TM(n->suffix))
                    strcpy(s, str_initcap_z(localized_abbrev_months[tm->tm_mon - 1], collid));
                else
                    strcpy(s, months[tm->tm_mon - 1]);
                s += strlen(s);
                break;
            case DCH_mon:
                INVALID_FOR_INTERVAL;
                if (!tm->tm_mon)
                    break;
                if (S_TM(n->suffix))
                    strcpy(s, str_tolower_z(localized_abbrev_months[tm->tm_mon - 1], collid));
                else
                    strcpy(s, asc_tolower_z(months[tm->tm_mon - 1]));
                s += strlen(s);
                break;
            case DCH_MM:
                sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_mon);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_DAY:
                INVALID_FOR_INTERVAL;
                if (S_TM(n->suffix))
                    strcpy(s, str_toupper_z(localized_full_days[tm->tm_wday], collid));
                else
                    sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
                            asc_toupper_z(days[tm->tm_wday]));
                s += strlen(s);
                break;
            case DCH_Day:
                INVALID_FOR_INTERVAL;
                if (S_TM(n->suffix))
                    strcpy(s, str_initcap_z(localized_full_days[tm->tm_wday], collid));
                else
                    sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
                            days[tm->tm_wday]);
                s += strlen(s);
                break;
            case DCH_day:
                INVALID_FOR_INTERVAL;
                if (S_TM(n->suffix))
                    strcpy(s, str_tolower_z(localized_full_days[tm->tm_wday], collid));
                else
                    sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
                            asc_tolower_z(days[tm->tm_wday]));
                s += strlen(s);
                break;
            case DCH_DY:
                INVALID_FOR_INTERVAL;
                if (S_TM(n->suffix))
                    strcpy(s, str_toupper_z(localized_abbrev_days[tm->tm_wday], collid));
                else
                    strcpy(s, asc_toupper_z(days_short[tm->tm_wday]));
                s += strlen(s);
                break;
            case DCH_Dy:
                INVALID_FOR_INTERVAL;
                if (S_TM(n->suffix))
                    strcpy(s, str_initcap_z(localized_abbrev_days[tm->tm_wday], collid));
                else
                    strcpy(s, days_short[tm->tm_wday]);
                s += strlen(s);
                break;
            case DCH_dy:
                INVALID_FOR_INTERVAL;
                if (S_TM(n->suffix))
                    strcpy(s, str_tolower_z(localized_abbrev_days[tm->tm_wday], collid));
                else
                    strcpy(s, asc_tolower_z(days_short[tm->tm_wday]));
                s += strlen(s);
                break;
            case DCH_DDD:
            case DCH_IDDD:
                sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 3,
                        (n->key->id == DCH_DDD) ?
                        tm->tm_yday :
                      date2isoyearday(tm->tm_year, tm->tm_mon, tm->tm_mday));
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_DD:
                sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_mday);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_D:
                INVALID_FOR_INTERVAL;
                sprintf(s, "%d", tm->tm_wday + 1);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_ID:
                INVALID_FOR_INTERVAL;
                sprintf(s, "%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_WW:
                sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
                        (tm->tm_yday - 1) / 7 + 1);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_IW:
                sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
                        date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday));
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_Q:
                if (!tm->tm_mon)
                    break;
                sprintf(s, "%d", (tm->tm_mon - 1) / 3 + 1);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_CC:
                if (is_interval)    /* straight calculation */
                    i = tm->tm_year / 100;
                else
                {
                    if (tm->tm_year > 0)
                        /* Century 20 == 1901 - 2000 */
                        i = (tm->tm_year - 1) / 100 + 1;
                    else
                        /* Century 6BC == 600BC - 501BC */
                        i = tm->tm_year / 100 - 1;
                }
                if (i <= 99 && i >= -99)
                    sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, i);
                else
                    sprintf(s, "%d", i);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_Y_YYY:
                i = ADJUST_YEAR(tm->tm_year, is_interval) / 1000;
                sprintf(s, "%d,%03d", i,
                        ADJUST_YEAR(tm->tm_year, is_interval) - (i * 1000));
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_YYYY:
            case DCH_IYYY:
                sprintf(s, "%0*d",
                        S_FM(n->suffix) ? 0 : 4,
                        (n->key->id == DCH_YYYY ?
                         ADJUST_YEAR(tm->tm_year, is_interval) :
                         ADJUST_YEAR(date2isoyear(tm->tm_year,
                                                  tm->tm_mon,
                                                  tm->tm_mday),
                                     is_interval)));
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_YYY:
            case DCH_IYY:
                sprintf(s, "%0*d",
                        S_FM(n->suffix) ? 0 : 3,
                        (n->key->id == DCH_YYY ?
                         ADJUST_YEAR(tm->tm_year, is_interval) :
                         ADJUST_YEAR(date2isoyear(tm->tm_year,
                                                  tm->tm_mon,
                                                  tm->tm_mday),
                                     is_interval)) % 1000);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_YY:
            case DCH_IY:
                sprintf(s, "%0*d",
                        S_FM(n->suffix) ? 0 : 2,
                        (n->key->id == DCH_YY ?
                         ADJUST_YEAR(tm->tm_year, is_interval) :
                         ADJUST_YEAR(date2isoyear(tm->tm_year,
                                                  tm->tm_mon,
                                                  tm->tm_mday),
                                     is_interval)) % 100);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_Y:
            case DCH_I:
                sprintf(s, "%1d",
                        (n->key->id == DCH_Y ?
                         ADJUST_YEAR(tm->tm_year, is_interval) :
                         ADJUST_YEAR(date2isoyear(tm->tm_year,
                                                  tm->tm_mon,
                                                  tm->tm_mday),
                                     is_interval)) % 10);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_RM:
                if (!tm->tm_mon)
                    break;
                sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4,
                        rm_months_upper[MONTHS_PER_YEAR - tm->tm_mon]);
                s += strlen(s);
                break;
            case DCH_rm:
                if (!tm->tm_mon)
                    break;
                sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -4,
                        rm_months_lower[MONTHS_PER_YEAR - tm->tm_mon]);
                s += strlen(s);
                break;
            case DCH_W:
                sprintf(s, "%d", (tm->tm_mday - 1) / 7 + 1);
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
            case DCH_J:
                sprintf(s, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
                if (S_THth(n->suffix))
                    str_numth(s, s, S_TH_TYPE(n->suffix));
                s += strlen(s);
                break;
        }
    }

    *s = '\0';
}

static void do_to_timestamp ( text date_txt,
text fmt,
struct pg_tm tm,
fsec_t fsec 
) [static]

Definition at line 3425 of file formatting.c.

References TmFromChar::bc, TmFromChar::cc, TmFromChar::clock, CLOCK_12_HOUR, TmFromChar::d, DCH_cache_getnew(), DCH_cache_search(), DCH_CACHE_SIZE, DCH_from_char(), DCH_index, DCH_TYPE, TmFromChar::dd, TmFromChar::ddd, DEBUG_TM, DEBUG_TMFC, ereport, errcode(), errhint(), errmsg(), ERROR, DCHCacheEntry::format, format, FROM_CHAR_DATE_ISOWEEK, TmFromChar::hh, HOURS_PER_DAY, i, isleap, isoweek2date(), isoweek2j(), isoweekdate2date(), TmFromChar::j, j2date(), TmFromChar::mi, TmFromChar::mm, TmFromChar::mode, TmFromChar::ms, NODE_TYPE_END, NULL, palloc(), parse_format(), pfree(), TmFromChar::pm, TmFromChar::ss, TmFromChar::ssss, text_to_cstring(), pg_tm::tm_hour, pg_tm::tm_mday, pg_tm::tm_min, pg_tm::tm_mon, pg_tm::tm_sec, pg_tm::tm_wday, pg_tm::tm_yday, pg_tm::tm_year, TmFromChar::us, VARSIZE_ANY_EXHDR, TmFromChar::w, TmFromChar::ww, TmFromChar::year, TmFromChar::yysz, ZERO_tm, and ZERO_tmfc.

Referenced by to_date(), and to_timestamp().

{
    FormatNode *format;
    TmFromChar  tmfc;
    int         fmt_len;

    ZERO_tmfc(&tmfc);
    ZERO_tm(tm);
    *fsec = 0;

    fmt_len = VARSIZE_ANY_EXHDR(fmt);

    if (fmt_len)
    {
        char       *fmt_str;
        char       *date_str;
        bool        incache;

        fmt_str = text_to_cstring(fmt);

        /*
         * Allocate new memory if format picture is bigger than static cache
         * and not use cache (call parser always)
         */
        if (fmt_len > DCH_CACHE_SIZE)
        {
            format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
            incache = FALSE;

            parse_format(format, fmt_str, DCH_keywords,
                         DCH_suff, DCH_index, DCH_TYPE, NULL);

            (format + fmt_len)->type = NODE_TYPE_END;   /* Paranoia? */
        }
        else
        {
            /*
             * Use cache buffers
             */
            DCHCacheEntry *ent;

            incache = TRUE;

            if ((ent = DCH_cache_search(fmt_str)) == NULL)
            {
                ent = DCH_cache_getnew(fmt_str);

                /*
                 * Not in the cache, must run parser and save a new
                 * format-picture to the cache.
                 */
                parse_format(ent->format, fmt_str, DCH_keywords,
                             DCH_suff, DCH_index, DCH_TYPE, NULL);

                (ent->format + fmt_len)->type = NODE_TYPE_END;  /* Paranoia? */
#ifdef DEBUG_TO_FROM_CHAR
                /* dump_node(ent->format, fmt_len); */
                /* dump_index(DCH_keywords, DCH_index); */
#endif
            }
            format = ent->format;
        }

#ifdef DEBUG_TO_FROM_CHAR
        /* dump_node(format, fmt_len); */
#endif

        date_str = text_to_cstring(date_txt);

        DCH_from_char(format, date_str, &tmfc);

        pfree(date_str);
        pfree(fmt_str);
        if (!incache)
            pfree(format);
    }

    DEBUG_TMFC(&tmfc);

    /*
     * Convert values that user define for FROM_CHAR (to_date/to_timestamp) to
     * standard 'tm'
     */
    if (tmfc.ssss)
    {
        int         x = tmfc.ssss;

        tm->tm_hour = x / SECS_PER_HOUR;
        x %= SECS_PER_HOUR;
        tm->tm_min = x / SECS_PER_MINUTE;
        x %= SECS_PER_MINUTE;
        tm->tm_sec = x;
    }

    if (tmfc.ss)
        tm->tm_sec = tmfc.ss;
    if (tmfc.mi)
        tm->tm_min = tmfc.mi;
    if (tmfc.hh)
        tm->tm_hour = tmfc.hh;

    if (tmfc.clock == CLOCK_12_HOUR)
    {
        if (tm->tm_hour < 1 || tm->tm_hour > HOURS_PER_DAY / 2)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                     errmsg("hour \"%d\" is invalid for the 12-hour clock",
                            tm->tm_hour),
                     errhint("Use the 24-hour clock, or give an hour between 1 and 12.")));

        if (tmfc.pm && tm->tm_hour < HOURS_PER_DAY / 2)
            tm->tm_hour += HOURS_PER_DAY / 2;
        else if (!tmfc.pm && tm->tm_hour == HOURS_PER_DAY / 2)
            tm->tm_hour = 0;
    }

    if (tmfc.year)
    {
        /*
         * If CC and YY (or Y) are provided, use YY as 2 low-order digits for
         * the year in the given century.  Keep in mind that the 21st century
         * AD runs from 2001-2100, not 2000-2099; 6th century BC runs from
         * 600BC to 501BC.
         */
        if (tmfc.cc && tmfc.yysz <= 2)
        {
            if (tmfc.bc)
                tmfc.cc = -tmfc.cc;
            tm->tm_year = tmfc.year % 100;
            if (tm->tm_year)
            {
                if (tmfc.cc >= 0)
                    tm->tm_year += (tmfc.cc - 1) * 100;
                else
                    tm->tm_year = (tmfc.cc + 1) * 100 - tm->tm_year + 1;
            }
            else
                /* find century year for dates ending in "00" */
                tm->tm_year = tmfc.cc * 100 + ((tmfc.cc >= 0) ? 0 : 1);         
        }
        else
        /* If a 4-digit year is provided, we use that and ignore CC. */
        {
            tm->tm_year = tmfc.year;
            if (tmfc.bc && tm->tm_year > 0)
                tm->tm_year = -(tm->tm_year - 1);
        }
    }
    else if (tmfc.cc)   /* use first year of century */
    {
        if (tmfc.bc)
            tmfc.cc = -tmfc.cc;
        if (tmfc.cc >= 0)
            /* +1 becuase 21st century started in 2001 */
            tm->tm_year = (tmfc.cc - 1) * 100 + 1;
        else
            /* +1 because year == 599 is 600 BC */
            tm->tm_year = tmfc.cc * 100 + 1;
    }

    if (tmfc.j)
        j2date(tmfc.j, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);

    if (tmfc.ww)
    {
        if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
        {
            /*
             * If tmfc.d is not set, then the date is left at the beginning of
             * the ISO week (Monday).
             */
            if (tmfc.d)
                isoweekdate2date(tmfc.ww, tmfc.d, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
            else
                isoweek2date(tmfc.ww, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
        }
        else
            tmfc.ddd = (tmfc.ww - 1) * 7 + 1;
    }

    if (tmfc.w)
        tmfc.dd = (tmfc.w - 1) * 7 + 1;
    if (tmfc.d)
        tm->tm_wday = tmfc.d - 1;   /* convert to native numbering */
    if (tmfc.dd)
        tm->tm_mday = tmfc.dd;
    if (tmfc.ddd)
        tm->tm_yday = tmfc.ddd;
    if (tmfc.mm)
        tm->tm_mon = tmfc.mm;

    if (tmfc.ddd && (tm->tm_mon <= 1 || tm->tm_mday <= 1))
    {
        /*
         * The month and day field have not been set, so we use the
         * day-of-year field to populate them.  Depending on the date mode,
         * this field may be interpreted as a Gregorian day-of-year, or an ISO
         * week date day-of-year.
         */

        if (!tm->tm_year && !tmfc.bc)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
            errmsg("cannot calculate day of year without year information")));

        if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
        {
            int         j0;     /* zeroth day of the ISO year, in Julian */

            j0 = isoweek2j(tm->tm_year, 1) - 1;

            j2date(j0 + tmfc.ddd, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
        }
        else
        {
            const int  *y;
            int         i;

            static const int ysum[2][13] = {
                {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
            {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};

            y = ysum[isleap(tm->tm_year)];

            for (i = 1; i <= MONTHS_PER_YEAR; i++)
            {
                if (tmfc.ddd < y[i])
                    break;
            }
            if (tm->tm_mon <= 1)
                tm->tm_mon = i;

            if (tm->tm_mday <= 1)
                tm->tm_mday = tmfc.ddd - y[i - 1];
        }
    }

#ifdef HAVE_INT64_TIMESTAMP
    if (tmfc.ms)
        *fsec += tmfc.ms * 1000;
    if (tmfc.us)
        *fsec += tmfc.us;
#else
    if (tmfc.ms)
        *fsec += (double) tmfc.ms / 1000;
    if (tmfc.us)
        *fsec += (double) tmfc.us / 1000000;
#endif

    DEBUG_TM(tm);
}

static char * fill_str ( char *  str,
int  c,
int  max 
) [static]

Definition at line 3685 of file formatting.c.

Referenced by float4_to_char(), float8_to_char(), int4_to_char(), int8_to_char(), int_to_roman(), and numeric_to_char().

{
    memset(str, c, max);
    *(str + max) = '\0';
    return str;
}

Datum float4_to_char ( PG_FUNCTION_ARGS   ) 

Definition at line 5273 of file formatting.c.

References fill_str(), format, int_to_roman(), IS_EEEE, is_infinite(), IS_MULTI, IS_ROMAN, MAXDOUBLEWIDTH, MAXFLOATWIDTH, NUMDesc::multi, palloc(), PG_GETARG_FLOAT4, PG_GETARG_TEXT_P, PG_RETURN_TEXT_P, NUMDesc::post, NUMDesc::pre, rint(), sign, snprintf(), val, and value.

{
    float4      value = PG_GETARG_FLOAT4(0);
    text       *fmt = PG_GETARG_TEXT_P(1);
    NUMDesc     Num;
    FormatNode *format;
    text       *result;
    bool        shouldFree;
    int         len = 0,
                plen = 0,
                sign = 0;
    char       *numstr,
               *orgnum,
               *p;

    NUM_TOCHAR_prepare;

    if (IS_ROMAN(&Num))
        numstr = orgnum = int_to_roman((int) rint(value));
    else if (IS_EEEE(&Num))
    {
        numstr = orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
        if (isnan(value) || is_infinite(value))
        {
            /*
             * Allow 6 characters for the leading sign, the decimal point,
             * "e", the exponent's sign and two exponent digits.
             */
            numstr = (char *) palloc(Num.pre + Num.post + 7);
            fill_str(numstr, '#', Num.pre + Num.post + 6);
            *numstr = ' ';
            *(numstr + Num.pre + 1) = '.';
        }
        else
        {
            snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, value);

            /*
             * Swap a leading positive sign for a space.
             */
            if (*orgnum == '+')
                *orgnum = ' ';

            len = strlen(orgnum);
            numstr = orgnum;
        }
    }
    else
    {
        float4      val = value;

        if (IS_MULTI(&Num))
        {
            float       multi = pow((double) 10, (double) Num.multi);

            val = value * multi;
            Num.pre += Num.multi;
        }

        orgnum = (char *) palloc(MAXFLOATWIDTH + 1);
        snprintf(orgnum, MAXFLOATWIDTH + 1, "%.0f", fabs(val));
        len = strlen(orgnum);
        if (Num.pre > len)
            plen = Num.pre - len;
        if (len >= FLT_DIG)
            Num.post = 0;
        else if (Num.post + len > FLT_DIG)
            Num.post = FLT_DIG - len;
        snprintf(orgnum, MAXFLOATWIDTH + 1, "%.*f", Num.post, val);

        if (*orgnum == '-')
        {                       /* < 0 */
            sign = '-';
            numstr = orgnum + 1;
        }
        else
        {
            sign = '+';
            numstr = orgnum;
        }
        if ((p = strchr(numstr, '.')))
            len = p - numstr;
        else
            len = strlen(numstr);

        if (Num.pre > len)
            plen = Num.pre - len;
        else if (len > Num.pre)
        {
            numstr = (char *) palloc(Num.pre + Num.post + 2);
            fill_str(numstr, '#', Num.pre + Num.post + 1);
            *(numstr + Num.pre) = '.';
        }
    }

Datum float8_to_char ( PG_FUNCTION_ARGS   ) 

Definition at line 5377 of file formatting.c.

References fill_str(), format, int_to_roman(), IS_EEEE, is_infinite(), IS_MULTI, IS_ROMAN, MAXDOUBLEWIDTH, NUMDesc::multi, palloc(), PG_GETARG_FLOAT8, PG_GETARG_TEXT_P, PG_RETURN_TEXT_P, NUMDesc::post, NUMDesc::pre, rint(), sign, snprintf(), val, and value.

{
    float8      value = PG_GETARG_FLOAT8(0);
    text       *fmt = PG_GETARG_TEXT_P(1);
    NUMDesc     Num;
    FormatNode *format;
    text       *result;
    bool        shouldFree;
    int         len = 0,
                plen = 0,
                sign = 0;
    char       *numstr,
               *orgnum,
               *p;

    NUM_TOCHAR_prepare;

    if (IS_ROMAN(&Num))
        numstr = orgnum = int_to_roman((int) rint(value));
    else if (IS_EEEE(&Num))
    {
        numstr = orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
        if (isnan(value) || is_infinite(value))
        {
            /*
             * Allow 6 characters for the leading sign, the decimal point,
             * "e", the exponent's sign and two exponent digits.
             */
            numstr = (char *) palloc(Num.pre + Num.post + 7);
            fill_str(numstr, '#', Num.pre + Num.post + 6);
            *numstr = ' ';
            *(numstr + Num.pre + 1) = '.';
        }
        else
        {
            snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, value);

            /*
             * Swap a leading positive sign for a space.
             */
            if (*orgnum == '+')
                *orgnum = ' ';

            len = strlen(orgnum);
            numstr = orgnum;
        }
    }
    else
    {
        float8      val = value;

        if (IS_MULTI(&Num))
        {
            double      multi = pow((double) 10, (double) Num.multi);

            val = value * multi;
            Num.pre += Num.multi;
        }
        orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
        len = snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%.0f", fabs(val));
        if (Num.pre > len)
            plen = Num.pre - len;
        if (len >= DBL_DIG)
            Num.post = 0;
        else if (Num.post + len > DBL_DIG)
            Num.post = DBL_DIG - len;
        snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%.*f", Num.post, val);

        if (*orgnum == '-')
        {                       /* < 0 */
            sign = '-';
            numstr = orgnum + 1;
        }
        else
        {
            sign = '+';
            numstr = orgnum;
        }
        if ((p = strchr(numstr, '.')))
            len = p - numstr;
        else
            len = strlen(numstr);

        if (Num.pre > len)
            plen = Num.pre - len;
        else if (len > Num.pre)
        {
            numstr = (char *) palloc(Num.pre + Num.post + 2);
            fill_str(numstr, '#', Num.pre + Num.post + 1);
            *(numstr + Num.pre) = '.';
        }
    }

static int from_char_parse_int ( int *  dest,
char **  src,
FormatNode node 
) [static]

Definition at line 2260 of file formatting.c.

References from_char_parse_int_len(), FormatNode::key, and KeyWord::len.

Referenced by DCH_from_char().

{
    return from_char_parse_int_len(dest, src, node->key->len, node);
}

static int from_char_parse_int_len ( int *  dest,
char **  src,
const int  len,
FormatNode node 
) [static]

Definition at line 2170 of file formatting.c.

References Assert, DCH_MAX_ITEM_SIZ, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, from_char_set_int(), init(), is_next_separator(), FormatNode::key, KeyWord::name, NULL, S_FM, strlcpy(), strspace_len(), and FormatNode::suffix.

Referenced by DCH_from_char(), and from_char_parse_int().

{
    long        result;
    char        copy[DCH_MAX_ITEM_SIZ + 1];
    char       *init = *src;
    int         used;

    /*
     * Skip any whitespace before parsing the integer.
     */
    *src += strspace_len(*src);

    Assert(len <= DCH_MAX_ITEM_SIZ);
    used = (int) strlcpy(copy, *src, len + 1);

    if (S_FM(node->suffix) || is_next_separator(node))
    {
        /*
         * This node is in Fill Mode, or the next node is known to be a
         * non-digit value, so we just slurp as many characters as we can get.
         */
        errno = 0;
        result = strtol(init, src, 10);
    }
    else
    {
        /*
         * We need to pull exactly the number of characters given in 'len' out
         * of the string, and convert those.
         */
        char       *last;

        if (used < len)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                errmsg("source string too short for \"%s\" formatting field",
                       node->key->name),
                     errdetail("Field requires %d characters, but only %d "
                               "remain.",
                               len, used),
                     errhint("If your source string is not fixed-width, try "
                             "using the \"FM\" modifier.")));

        errno = 0;
        result = strtol(copy, &last, 10);
        used = last - copy;

        if (used > 0 && used < len)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                     errmsg("invalid value \"%s\" for \"%s\"",
                            copy, node->key->name),
                     errdetail("Field requires %d characters, but only %d "
                               "could be parsed.", len, used),
                     errhint("If your source string is not fixed-width, try "
                             "using the \"FM\" modifier.")));

        *src += used;
    }

    if (*src == init)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                 errmsg("invalid value \"%s\" for \"%s\"",
                        copy, node->key->name),
                 errdetail("Value must be an integer.")));

    if (errno == ERANGE || result < INT_MIN || result > INT_MAX)
        ereport(ERROR,
                (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                 errmsg("value for \"%s\" in source string is out of range",
                        node->key->name),
                 errdetail("Value must be in the range %d to %d.",
                           INT_MIN, INT_MAX)));

    if (dest != NULL)
        from_char_set_int(dest, (int) result, node);
    return *src - init;
}

static int from_char_seq_search ( int *  dest,
char **  src,
char **  array,
int  type,
int  max,
FormatNode node 
) [static]

Definition at line 2348 of file formatting.c.

References Assert, DCH_MAX_ITEM_SIZ, ereport, errcode(), errdetail(), errmsg(), ERROR, FormatNode::key, KeyWord::name, seq_search(), and strlcpy().

Referenced by DCH_from_char().

{
    int         len;

    *dest = seq_search(*src, array, type, max, &len);
    if (len <= 0)
    {
        char        copy[DCH_MAX_ITEM_SIZ + 1];

        Assert(max <= DCH_MAX_ITEM_SIZ);
        strlcpy(copy, *src, max + 1);

        ereport(ERROR,
                (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                 errmsg("invalid value \"%s\" for \"%s\"",
                        copy, node->key->name),
                 errdetail("The given value did not match any of the allowed "
                           "values for this field.")));
    }
    *src += len;
    return len;
}

static void from_char_set_int ( int *  dest,
const int  value,
const FormatNode node 
) [static]

Definition at line 2137 of file formatting.c.

References ereport, errcode(), errdetail(), errmsg(), ERROR, FormatNode::key, and KeyWord::name.

Referenced by DCH_from_char(), and from_char_parse_int_len().

{
    if (*dest != 0 && *dest != value)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
           errmsg("conflicting values for \"%s\" field in formatting string",
                  node->key->name),
                 errdetail("This value contradicts a previous setting for "
                           "the same field type.")));
    *dest = value;
}

static void from_char_set_mode ( TmFromChar tmfc,
const FromCharDateMode  mode 
) [static]

Definition at line 2115 of file formatting.c.

References ereport, errcode(), errhint(), errmsg(), ERROR, FROM_CHAR_DATE_NONE, and TmFromChar::mode.

Referenced by DCH_from_char().

{
    if (mode != FROM_CHAR_DATE_NONE)
    {
        if (tmfc->mode == FROM_CHAR_DATE_NONE)
            tmfc->mode = mode;
        else if (tmfc->mode != mode)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
                     errmsg("invalid combination of date conventions"),
                     errhint("Do not mix Gregorian and ISO week date "
                             "conventions in a formatting template.")));
    }
}

static char * get_last_relevant_decnum ( char *  num  )  [static]

Definition at line 4018 of file formatting.c.

References elog.

Referenced by NUM_processor().

{
    char       *result,
               *p = strchr(num, '.');

#ifdef DEBUG_TO_FROM_CHAR
    elog(DEBUG_elog_output, "get_last_relevant_decnum()");
#endif

    if (!p)
        return NULL;

    result = p;

    while (*(++p))
    {
        if (*p != '0')
            result = p;
    }

    return result;
}

static char * get_th ( char *  num,
int  type 
) [static]

Definition at line 1404 of file formatting.c.

References ereport, errcode(), errmsg(), ERROR, numth, numTH, and TH_UPPER.

Referenced by NUM_processor(), and str_numth().

{
    int         len = strlen(num),
                last,
                seclast;

    last = *(num + (len - 1));
    if (!isdigit((unsigned char) last))
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                 errmsg("\"%s\" is not a number", num)));

    /*
     * All "teens" (<x>1[0-9]) get 'TH/th', while <x>[02-9][123] still get
     * 'ST/st', 'ND/nd', 'RD/rd', respectively
     */
    if ((len > 1) && ((seclast = num[len - 2]) == '1'))
        last = 0;

    switch (last)
    {
        case '1':
            if (type == TH_UPPER)
                return numTH[0];
            return numth[0];
        case '2':
            if (type == TH_UPPER)
                return numTH[1];
            return numth[1];
        case '3':
            if (type == TH_UPPER)
                return numTH[2];
            return numth[2];
        default:
            if (type == TH_UPPER)
                return numTH[3];
            return numth[3];
    }
}

static const KeyWord * index_seq_search ( char *  str,
const KeyWord kw,
const int *  index 
) [static]

Definition at line 1002 of file formatting.c.

References KeyWord_INDEX_FILTER, KeyWord::len, and KeyWord::name.

Referenced by parse_format().

{
    int         poz;

    if (!KeyWord_INDEX_FILTER(*str))
        return NULL;

    if ((poz = *(index + (*str - ' '))) > -1)
    {
        const KeyWord *k = kw + poz;

        do
        {
            if (strncmp(str, k->name, k->len) == 0)
                return k;
            k++;
            if (!k->name)
                return NULL;
        } while (*str == *k->name);
    }
    return NULL;
}

Datum int4_to_char ( PG_FUNCTION_ARGS   ) 

Definition at line 5075 of file formatting.c.

References DatumGetCString, DirectFunctionCall1, fill_str(), format, Int32GetDatum, int4out(), int_to_roman(), IS_EEEE, IS_MULTI, IS_ROMAN, MAXDOUBLEWIDTH, NUMDesc::multi, palloc(), PG_GETARG_INT32, PG_GETARG_TEXT_P, PG_RETURN_TEXT_P, NUMDesc::post, NUMDesc::pre, sign, snprintf(), val, and value.

{
    int32       value = PG_GETARG_INT32(0);
    text       *fmt = PG_GETARG_TEXT_P(1);
    NUMDesc     Num;
    FormatNode *format;
    text       *result;
    bool        shouldFree;
    int         len = 0,
                plen = 0,
                sign = 0;
    char       *numstr,
               *orgnum;

    NUM_TOCHAR_prepare;

    /*
     * On DateType depend part (int32)
     */
    if (IS_ROMAN(&Num))
        numstr = orgnum = int_to_roman(value);
    else if (IS_EEEE(&Num))
    {
        /* we can do it easily because float8 won't lose any precision */
        float8      val = (float8) value;

        orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
        snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, val);

        /*
         * Swap a leading positive sign for a space.
         */
        if (*orgnum == '+')
            *orgnum = ' ';

        len = strlen(orgnum);
        numstr = orgnum;
    }
    else
    {
        if (IS_MULTI(&Num))
        {
            orgnum = DatumGetCString(DirectFunctionCall1(int4out,
                                                         Int32GetDatum(value * ((int32) pow((double) 10, (double) Num.multi)))));
            Num.pre += Num.multi;
        }
        else
        {
            orgnum = DatumGetCString(DirectFunctionCall1(int4out,
                                                      Int32GetDatum(value)));
        }

        if (*orgnum == '-')
        {
            sign = '-';
            orgnum++;
        }
        else
            sign = '+';
        len = strlen(orgnum);

        if (Num.post)
        {
            numstr = (char *) palloc(len + Num.post + 2);
            strcpy(numstr, orgnum);
            *(numstr + len) = '.';
            memset(numstr + len + 1, '0', Num.post);
            *(numstr + len + Num.post + 1) = '\0';
        }
        else
            numstr = orgnum;

        if (Num.pre > len)
            plen = Num.pre - len;
        else if (len > Num.pre)
        {
            numstr = (char *) palloc(Num.pre + Num.post + 2);
            fill_str(numstr, '#', Num.pre + Num.post + 1);
            *(numstr + Num.pre) = '.';
        }
    }

Datum int8_to_char ( PG_FUNCTION_ARGS   ) 

Definition at line 5166 of file formatting.c.

References DatumGetCString, DatumGetInt32, DatumGetInt64, DatumGetNumeric, DirectFunctionCall1, DirectFunctionCall2, dtoi8(), fill_str(), Float8GetDatum(), format, Int64GetDatum(), int84(), int8_numeric(), int8mul(), int8out(), int_to_roman(), IS_EEEE, IS_MULTI, IS_ROMAN, NUMDesc::multi, numeric_out_sci(), palloc(), PG_GETARG_INT64, PG_GETARG_TEXT_P, PG_RETURN_TEXT_P, NUMDesc::post, NUMDesc::pre, sign, val, and value.

{
    int64       value = PG_GETARG_INT64(0);
    text       *fmt = PG_GETARG_TEXT_P(1);
    NUMDesc     Num;
    FormatNode *format;
    text       *result;
    bool        shouldFree;
    int         len = 0,
                plen = 0,
                sign = 0;
    char       *numstr,
               *orgnum;

    NUM_TOCHAR_prepare;

    /*
     * On DateType depend part (int32)
     */
    if (IS_ROMAN(&Num))
    {
        /* Currently don't support int8 conversion to roman... */
        numstr = orgnum = int_to_roman(DatumGetInt32(
                          DirectFunctionCall1(int84, Int64GetDatum(value))));
    }
    else if (IS_EEEE(&Num))
    {
        /* to avoid loss of precision, must go via numeric not float8 */
        Numeric     val;

        val = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
                                                  Int64GetDatum(value)));
        orgnum = numeric_out_sci(val, Num.post);

        /*
         * numeric_out_sci() does not emit a sign for positive numbers.  We
         * need to add a space in this case so that positive and negative
         * numbers are aligned.  We don't have to worry about NaN here.
         */
        if (*orgnum != '-')
        {
            numstr = (char *) palloc(strlen(orgnum) + 2);
            *numstr = ' ';
            strcpy(numstr + 1, orgnum);
            len = strlen(numstr);
        }
        else
        {
            numstr = orgnum;
            len = strlen(orgnum);
        }
    }
    else
    {
        if (IS_MULTI(&Num))
        {
            double      multi = pow((double) 10, (double) Num.multi);

            value = DatumGetInt64(DirectFunctionCall2(int8mul,
                                                      Int64GetDatum(value),
                                                   DirectFunctionCall1(dtoi8,
                                                    Float8GetDatum(multi))));
            Num.pre += Num.multi;
        }

        orgnum = DatumGetCString(DirectFunctionCall1(int8out,
                                                     Int64GetDatum(value)));

        if (*orgnum == '-')
        {
            sign = '-';
            orgnum++;
        }
        else
            sign = '+';
        len = strlen(orgnum);

        if (Num.post)
        {
            numstr = (char *) palloc(len + Num.post + 2);
            strcpy(numstr, orgnum);
            *(numstr + len) = '.';
            memset(numstr + len + 1, '0', Num.post);
            *(numstr + len + Num.post + 1) = '\0';
        }
        else
            numstr = orgnum;

        if (Num.pre > len)
            plen = Num.pre - len;
        else if (len > Num.pre)
        {
            numstr = (char *) palloc(Num.pre + Num.post + 2);
            fill_str(numstr, '#', Num.pre + Num.post + 1);
            *(numstr + Num.pre) = '.';
        }
    }

static char * int_to_roman ( int  number  )  [static]

Definition at line 3887 of file formatting.c.

References fill_str(), palloc(), rm1, rm10, rm100, and snprintf().

Referenced by float4_to_char(), float8_to_char(), int4_to_char(), int8_to_char(), and numeric_to_char().

{
    int         len = 0,
                num = 0;
    char       *p = NULL,
               *result,
                numstr[5];

    result = (char *) palloc(16);
    *result = '\0';

    if (number > 3999 || number < 1)
    {
        fill_str(result, '#', 15);
        return result;
    }
    len = snprintf(numstr, sizeof(numstr), "%d", number);

    for (p = numstr; *p != '\0'; p++, --len)
    {
        num = *p - 49;          /* 48 ascii + 1 */
        if (num < 0)
            continue;

        if (len > 3)
        {
            while (num-- != -1)
                strcat(result, "M");
        }
        else
        {
            if (len == 3)
                strcat(result, rm100[num]);
            else if (len == 2)
                strcat(result, rm10[num]);
            else if (len == 1)
                strcat(result, rm1[num]);
        }
    }
    return result;
}

Datum interval_to_char ( PG_FUNCTION_ARGS   ) 

Definition at line 3326 of file formatting.c.

References datetime_to_char_body(), DAYS_PER_MONTH, interval2tm(), MONTHS_PER_YEAR, PG_GET_COLLATION, PG_GETARG_INTERVAL_P, PG_GETARG_TEXT_P, PG_RETURN_NULL, PG_RETURN_TEXT_P, tm, pg_tm::tm_mday, pg_tm::tm_mon, pg_tm::tm_yday, pg_tm::tm_year, tmtcFsec, tmtcTm, VARHDRSZ, VARSIZE, and ZERO_tmtc.

{
    Interval   *it = PG_GETARG_INTERVAL_P(0);
    text       *fmt = PG_GETARG_TEXT_P(1),
               *res;
    TmToChar    tmtc;
    struct pg_tm *tm;

    if ((VARSIZE(fmt) - VARHDRSZ) <= 0)
        PG_RETURN_NULL();

    ZERO_tmtc(&tmtc);
    tm = tmtcTm(&tmtc);

    if (interval2tm(*it, tm, &tmtcFsec(&tmtc)) != 0)
        PG_RETURN_NULL();

    /* wday is meaningless, yday approximates the total span in days */
    tm->tm_yday = (tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon) * DAYS_PER_MONTH + tm->tm_mday;

    if (!(res = datetime_to_char_body(&tmtc, fmt, true, PG_GET_COLLATION())))
        PG_RETURN_NULL();

    PG_RETURN_TEXT_P(res);
}

static bool is_next_separator ( FormatNode n  )  [static]

Definition at line 2023 of file formatting.c.

References FormatNode::character, KeyWord::is_digit, FormatNode::key, NODE_TYPE_ACTION, NODE_TYPE_END, S_THth, FormatNode::suffix, and FormatNode::type.

Referenced by from_char_parse_int_len().

{
    if (n->type == NODE_TYPE_END)
        return FALSE;

    if (n->type == NODE_TYPE_ACTION && S_THth(n->suffix))
        return TRUE;

    /*
     * Next node
     */
    n++;

    /* end of format string is treated like a non-digit separator */
    if (n->type == NODE_TYPE_END)
        return TRUE;

    if (n->type == NODE_TYPE_ACTION)
    {
        if (n->key->is_digit)
            return FALSE;

        return TRUE;
    }
    else if (isdigit((unsigned char) n->character))
        return FALSE;

    return TRUE;                /* some non-digit input (separator) */
}

static FormatNode * NUM_cache ( int  len,
NUMDesc Num,
text pars_str,
bool shouldFree 
) [static]

Definition at line 3812 of file formatting.c.

References NUMDesc::flag, NUMCacheEntry::format, format, NUMDesc::lsign, NUMDesc::multi, NUMDesc::need_locale, NODE_TYPE_END, NULL, NUMCacheEntry::Num, NUM_cache_getnew(), NUM_cache_search(), NUM_CACHE_SIZE, NUM_index, NUM_TYPE, palloc(), parse_format(), pfree(), NUMDesc::post, NUMDesc::pre, NUMDesc::pre_lsign_num, text_to_cstring(), NUMDesc::zero_end, NUMDesc::zero_start, and zeroize_NUM.

Referenced by numeric_to_number().

{
    FormatNode *format = NULL;
    char       *str;

    str = text_to_cstring(pars_str);

    /*
     * Allocate new memory if format picture is bigger than static cache and
     * not use cache (call parser always). This branches sets shouldFree to
     * true, accordingly.
     */
    if (len > NUM_CACHE_SIZE)
    {
        format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));

        *shouldFree = true;

        zeroize_NUM(Num);

        parse_format(format, str, NUM_keywords,
                     NULL, NUM_index, NUM_TYPE, Num);

        (format + len)->type = NODE_TYPE_END;   /* Paranoia? */
    }
    else
    {
        /*
         * Use cache buffers
         */
        NUMCacheEntry *ent;

        *shouldFree = false;

        if ((ent = NUM_cache_search(str)) == NULL)
        {
            ent = NUM_cache_getnew(str);

            /*
             * Not in the cache, must run parser and save a new format-picture
             * to the cache.
             */
            parse_format(ent->format, str, NUM_keywords,
                         NULL, NUM_index, NUM_TYPE, &ent->Num);

            (ent->format + len)->type = NODE_TYPE_END;  /* Paranoia? */
        }

        format = ent->format;

        /*
         * Copy cache to used struct
         */
        Num->flag = ent->Num.flag;
        Num->lsign = ent->Num.lsign;
        Num->pre = ent->Num.pre;
        Num->post = ent->Num.post;
        Num->pre_lsign_num = ent->Num.pre_lsign_num;
        Num->need_locale = ent->Num.need_locale;
        Num->multi = ent->Num.multi;
        Num->zero_start = ent->Num.zero_start;
        Num->zero_end = ent->Num.zero_end;
    }

#ifdef DEBUG_TO_FROM_CHAR
    /* dump_node(format, len); */
    dump_index(NUM_keywords, NUM_index);
#endif

    pfree(str);
    return format;
}

static NUMCacheEntry * NUM_cache_getnew ( char *  str  )  [static]

Definition at line 3706 of file formatting.c.

References NUMCacheEntry::age, elog, n_NUMCache, NUMCacheEntry::Num, NUM_CACHE_FIELDS, NUM_CACHE_SIZE, NUMCounter, NUMCacheEntry::str, StrNCpy, and zeroize_NUM.

Referenced by NUM_cache().

{
    NUMCacheEntry *ent;

    /* counter overflow check - paranoia? */
    if (NUMCounter >= (INT_MAX - NUM_CACHE_FIELDS - 1))
    {
        NUMCounter = 0;

        for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
            ent->age = (++NUMCounter);
    }

    /*
     * If cache is full, remove oldest entry
     */
    if (n_NUMCache > NUM_CACHE_FIELDS)
    {
        NUMCacheEntry *old = NUMCache + 0;

#ifdef DEBUG_TO_FROM_CHAR
        elog(DEBUG_elog_output, "Cache is full (%d)", n_NUMCache);
#endif
        for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
        {
            /*
             * entry removed via NUM_cache_remove() can be used here, which is
             * why it's worth scanning first entry again
             */
            if (ent->str[0] == '\0')
            {
                old = ent;
                break;
            }
            if (ent->age < old->age)
                old = ent;
        }
#ifdef DEBUG_TO_FROM_CHAR
        elog(DEBUG_elog_output, "OLD: \"%s\" AGE: %d", old->str, old->age);
#endif
        StrNCpy(old->str, str, NUM_CACHE_SIZE + 1);
        /* old->format fill parser */
        old->age = (++NUMCounter);
        ent = old;
    }
    else
    {
#ifdef DEBUG_TO_FROM_CHAR
        elog(DEBUG_elog_output, "NEW (%d)", n_NUMCache);
#endif
        ent = NUMCache + n_NUMCache;
        StrNCpy(ent->str, str, NUM_CACHE_SIZE + 1);
        /* ent->format fill parser */
        ent->age = (++NUMCounter);
        ++n_NUMCache;
    }

    zeroize_NUM(&ent->Num);

    last_NUMCacheEntry = ent;
    return ent;
}

static void NUM_cache_remove ( NUMCacheEntry ent  )  [static]

Definition at line 3798 of file formatting.c.

References NUMCacheEntry::age, elog, and NUMCacheEntry::str.

Referenced by NUMDesc_prepare().

{
#ifdef DEBUG_TO_FROM_CHAR
    elog(DEBUG_elog_output, "REMOVING ENTRY (%s)", ent->str);
#endif
    ent->str[0] = '\0';
    ent->age = 0;
}

static NUMCacheEntry * NUM_cache_search ( char *  str  )  [static]

Definition at line 3770 of file formatting.c.

References NUMCacheEntry::age, i, n_NUMCache, NUM_CACHE_FIELDS, NUMCounter, and NUMCacheEntry::str.

Referenced by NUM_cache().

{
    int         i;
    NUMCacheEntry *ent;

    /* counter overflow check - paranoia? */
    if (NUMCounter >= (INT_MAX - NUM_CACHE_FIELDS - 1))
    {
        NUMCounter = 0;

        for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
            ent->age = (++NUMCounter);
    }

    for (i = 0, ent = NUMCache; i < n_NUMCache; i++, ent++)
    {
        if (strcmp(ent->str, str) == 0)
        {
            ent->age = (++NUMCounter);
            last_NUMCacheEntry = ent;
            return ent;
        }
    }

    return NULL;
}

static void NUM_numpart_from_char ( NUMProc Np,
int  id,
int  plen 
) [static]

Definition at line 4046 of file formatting.c.

References AMOUNT_TEST, NUMProc::decimal, elog, FALSE, NUMProc::inout, NUMProc::inout_p, IS_BRACKET, IS_DECIMAL, IS_LSIGN, IS_MINUS, IS_PLUS, NUMProc::L_negative_sign, NUMProc::L_positive_sign, NUMDesc::lsign, NUMProc::Num, NUM_0, NUM_9, NUM_DEC, NUM_LSIGN_PRE, NUMProc::number, NUMProc::number_p, OVERLOAD_TEST, NUMDesc::post, NUMProc::read_dec, NUMProc::read_post, and NUMProc::read_pre.

Referenced by NUM_processor().

{
    bool        isread = FALSE;

#ifdef DEBUG_TO_FROM_CHAR
    elog(DEBUG_elog_output, " --- scan start --- id=%s",
         (id == NUM_0 || id == NUM_9) ? "NUM_0/9" : id == NUM_DEC ? "NUM_DEC" : "???");
#endif

    if (*Np->inout_p == ' ')
        Np->inout_p++;

#define OVERLOAD_TEST   (Np->inout_p >= Np->inout + plen)
#define AMOUNT_TEST(_s) (plen-(Np->inout_p-Np->inout) >= _s)

    if (*Np->inout_p == ' ')
        Np->inout_p++;

    if (OVERLOAD_TEST)
        return;

    /*
     * read sign before number
     */
    if (*Np->number == ' ' && (id == NUM_0 || id == NUM_9) &&
        (Np->read_pre + Np->read_post) == 0)
    {
#ifdef DEBUG_TO_FROM_CHAR
        elog(DEBUG_elog_output, "Try read sign (%c), locale positive: %s, negative: %s",
             *Np->inout_p, Np->L_positive_sign, Np->L_negative_sign);
#endif

        /*
         * locale sign
         */
        if (IS_LSIGN(Np->Num) && Np->Num->lsign == NUM_LSIGN_PRE)
        {
            int         x = 0;

#ifdef DEBUG_TO_FROM_CHAR
            elog(DEBUG_elog_output, "Try read locale pre-sign (%c)", *Np->inout_p);
#endif
            if ((x = strlen(Np->L_negative_sign)) &&
                AMOUNT_TEST(x) &&
                strncmp(Np->inout_p, Np->L_negative_sign, x) == 0)
            {
                Np->inout_p += x;
                *Np->number = '-';
            }
            else if ((x = strlen(Np->L_positive_sign)) &&
                     AMOUNT_TEST(x) &&
                     strncmp(Np->inout_p, Np->L_positive_sign, x) == 0)
            {
                Np->inout_p += x;
                *Np->number = '+';
            }
        }
        else
        {
#ifdef DEBUG_TO_FROM_CHAR
            elog(DEBUG_elog_output, "Try read simple sign (%c)", *Np->inout_p);
#endif

            /*
             * simple + - < >
             */
            if (*Np->inout_p == '-' || (IS_BRACKET(Np->Num) &&
                                        *Np->inout_p == '<'))
            {
                *Np->number = '-';      /* set - */
                Np->inout_p++;
            }
            else if (*Np->inout_p == '+')
            {
                *Np->number = '+';      /* set + */
                Np->inout_p++;
            }
        }
    }

    if (OVERLOAD_TEST)
        return;

#ifdef DEBUG_TO_FROM_CHAR
    elog(DEBUG_elog_output, "Scan for numbers (%c), current number: '%s'", *Np->inout_p, Np->number);
#endif

    /*
     * read digit
     */
    if (isdigit((unsigned char) *Np->inout_p))
    {
        if (Np->read_dec && Np->read_post == Np->Num->post)
            return;

        *Np->number_p = *Np->inout_p;
        Np->number_p++;

        if (Np->read_dec)
            Np->read_post++;
        else
            Np->read_pre++;

        isread = TRUE;

#ifdef DEBUG_TO_FROM_CHAR
        elog(DEBUG_elog_output, "Read digit (%c)", *Np->inout_p);
#endif

        /*
         * read decimal point
         */
    }
    else if (IS_DECIMAL(Np->Num) && Np->read_dec == FALSE)
    {
#ifdef DEBUG_TO_FROM_CHAR
        elog(DEBUG_elog_output, "Try read decimal point (%c)", *Np->inout_p);
#endif
        if (*Np->inout_p == '.')
        {
            *Np->number_p = '.';
            Np->number_p++;
            Np->read_dec = TRUE;
            isread = TRUE;
        }
        else
        {
            int         x = strlen(Np->decimal);

#ifdef DEBUG_TO_FROM_CHAR
            elog(DEBUG_elog_output, "Try read locale point (%c)",
                 *Np->inout_p);
#endif
            if (x && AMOUNT_TEST(x) && strncmp(Np->inout_p, Np->decimal, x) == 0)
            {
                Np->inout_p += x - 1;
                *Np->number_p = '.';
                Np->number_p++;
                Np->read_dec = TRUE;
                isread = TRUE;
            }
        }
    }

    if (OVERLOAD_TEST)
        return;

    /*
     * Read sign behind "last" number
     *
     * We need sign detection because determine exact position of post-sign is
     * difficult:
     *
     * FM9999.9999999S     -> 123.001- 9.9S            -> .5- FM9.999999MI ->
     * 5.01-
     */
    if (*Np->number == ' ' && Np->read_pre + Np->read_post > 0)
    {
        /*
         * locale sign (NUM_S) is always anchored behind a last number, if: -
         * locale sign expected - last read char was NUM_0/9 or NUM_DEC - and
         * next char is not digit
         */
        if (IS_LSIGN(Np->Num) && isread &&
            (Np->inout_p + 1) <= Np->inout + plen &&
            !isdigit((unsigned char) *(Np->inout_p + 1)))
        {
            int         x;
            char       *tmp = Np->inout_p++;

#ifdef DEBUG_TO_FROM_CHAR
            elog(DEBUG_elog_output, "Try read locale post-sign (%c)", *Np->inout_p);
#endif
            if ((x = strlen(Np->L_negative_sign)) &&
                AMOUNT_TEST(x) &&
                strncmp(Np->inout_p, Np->L_negative_sign, x) == 0)
            {
                Np->inout_p += x - 1;   /* -1 .. NUM_processor() do inout_p++ */
                *Np->number = '-';
            }
            else if ((x = strlen(Np->L_positive_sign)) &&
                     AMOUNT_TEST(x) &&
                     strncmp(Np->inout_p, Np->L_positive_sign, x) == 0)
            {
                Np->inout_p += x - 1;   /* -1 .. NUM_processor() do inout_p++ */
                *Np->number = '+';
            }
            if (*Np->number == ' ')
                /* no sign read */
                Np->inout_p = tmp;
        }

        /*
         * try read non-locale sign, it's happen only if format is not exact
         * and we cannot determine sign position of MI/PL/SG, an example:
         *
         * FM9.999999MI            -> 5.01-
         *
         * if (.... && IS_LSIGN(Np->Num)==FALSE) prevents read wrong formats
         * like to_number('1 -', '9S') where sign is not anchored to last
         * number.
         */
        else if (isread == FALSE && IS_LSIGN(Np->Num) == FALSE &&
                 (IS_PLUS(Np->Num) || IS_MINUS(Np->Num)))
        {
#ifdef DEBUG_TO_FROM_CHAR
            elog(DEBUG_elog_output, "Try read simple post-sign (%c)", *Np->inout_p);
#endif

            /*
             * simple + -
             */
            if (*Np->inout_p == '-' || *Np->inout_p == '+')
                /* NUM_processor() do inout_p++ */
                *Np->number = *Np->inout_p;
        }
    }
}

static void NUM_numpart_to_char ( NUMProc Np,
int  id 
) [static]

Definition at line 4276 of file formatting.c.

References NUMProc::decimal, elog, FALSE, NUMProc::inout, NUMProc::inout_p, IS_BRACKET, IS_DECIMAL, IS_FILLMODE, IS_LSIGN, IS_PREDEC_SPACE, IS_ROMAN, IS_ZERO, NUMProc::L_negative_sign, NUMProc::L_positive_sign, NUMProc::last_relevant, NUMDesc::lsign, NUMProc::Num, NUM_0, NUM_9, NUMProc::num_count, NUMProc::num_curr, NUM_D, NUM_DEC, NUMProc::num_in, NUM_LSIGN_PRE, NUMProc::num_pre, NUMProc::number_p, NUMProc::sign, NUMProc::sign_wrote, TRUE, and NUMDesc::zero_start.

Referenced by NUM_processor().

{
    int         end;

    if (IS_ROMAN(Np->Num))
        return;

    /* Note: in this elog() output not set '\0' in 'inout' */

#ifdef DEBUG_TO_FROM_CHAR

    /*
     * Np->num_curr is number of current item in format-picture, it is not
     * current position in inout!
     */
    elog(DEBUG_elog_output,
         "SIGN_WROTE: %d, CURRENT: %d, NUMBER_P: \"%s\", INOUT: \"%s\"",
         Np->sign_wrote,
         Np->num_curr,
         Np->number_p,
         Np->inout);
#endif
    Np->num_in = FALSE;

    /*
     * Write sign if real number will write to output Note: IS_PREDEC_SPACE()
     * handle "9.9" --> " .1"
     */
    if (Np->sign_wrote == FALSE &&
        (Np->num_curr >= Np->num_pre || (IS_ZERO(Np->Num) && Np->Num->zero_start == Np->num_curr)) &&
        (IS_PREDEC_SPACE(Np) == FALSE || (Np->last_relevant && *Np->last_relevant == '.')))
    {
        if (IS_LSIGN(Np->Num))
        {
            if (Np->Num->lsign == NUM_LSIGN_PRE)
            {
                if (Np->sign == '-')
                    strcpy(Np->inout_p, Np->L_negative_sign);
                else
                    strcpy(Np->inout_p, Np->L_positive_sign);
                Np->inout_p += strlen(Np->inout_p);
                Np->sign_wrote = TRUE;
            }
        }
        else if (IS_BRACKET(Np->Num))
        {
            *Np->inout_p = Np->sign == '+' ? ' ' : '<';
            ++Np->inout_p;
            Np->sign_wrote = TRUE;
        }
        else if (Np->sign == '+')
        {
            if (!IS_FILLMODE(Np->Num))
            {
                *Np->inout_p = ' ';     /* Write + */
                ++Np->inout_p;
            }
            Np->sign_wrote = TRUE;
        }
        else if (Np->sign == '-')
        {                       /* Write - */
            *Np->inout_p = '-';
            ++Np->inout_p;
            Np->sign_wrote = TRUE;
        }
    }


    /*
     * digits / FM / Zero / Dec. point
     */
    if (id == NUM_9 || id == NUM_0 || id == NUM_D || id == NUM_DEC)
    {
        if (Np->num_curr < Np->num_pre &&
            (Np->Num->zero_start > Np->num_curr || !IS_ZERO(Np->Num)))
        {
            /*
             * Write blank space
             */
            if (!IS_FILLMODE(Np->Num))
            {
                *Np->inout_p = ' ';     /* Write ' ' */
                ++Np->inout_p;
            }
        }
        else if (IS_ZERO(Np->Num) &&
                 Np->num_curr < Np->num_pre &&
                 Np->Num->zero_start <= Np->num_curr)
        {
            /*
             * Write ZERO
             */
            *Np->inout_p = '0'; /* Write '0' */
            ++Np->inout_p;
            Np->num_in = TRUE;
        }
        else
        {
            /*
             * Write Decimal point
             */
            if (*Np->number_p == '.')
            {
                if (!Np->last_relevant || *Np->last_relevant != '.')
                {
                    strcpy(Np->inout_p, Np->decimal);   /* Write DEC/D */
                    Np->inout_p += strlen(Np->inout_p);
                }

                /*
                 * Ora 'n' -- FM9.9 --> 'n.'
                 */
                else if (IS_FILLMODE(Np->Num) &&
                         Np->last_relevant && *Np->last_relevant == '.')
                {
                    strcpy(Np->inout_p, Np->decimal);   /* Write DEC/D */
                    Np->inout_p += strlen(Np->inout_p);
                }
            }
            else
            {
                /*
                 * Write Digits
                 */
                if (Np->last_relevant && Np->number_p > Np->last_relevant &&
                    id != NUM_0)
                    ;

                /*
                 * '0.1' -- 9.9 --> '  .1'
                 */
                else if (IS_PREDEC_SPACE(Np))
                {
                    if (!IS_FILLMODE(Np->Num))
                    {
                        *Np->inout_p = ' ';
                        ++Np->inout_p;
                    }

                    /*
                     * '0' -- FM9.9 --> '0.'
                     */
                    else if (Np->last_relevant && *Np->last_relevant == '.')
                    {
                        *Np->inout_p = '0';
                        ++Np->inout_p;
                    }
                }
                else
                {
                    *Np->inout_p = *Np->number_p;       /* Write DIGIT */
                    ++Np->inout_p;
                    Np->num_in = TRUE;
                }
            }
            ++Np->number_p;
        }

        end = Np->num_count + (Np->num_pre ? 1 : 0) + (IS_DECIMAL(Np->Num) ? 1 : 0);

        if (Np->last_relevant && Np->last_relevant == Np->number_p)
            end = Np->num_curr;

        if (Np->num_curr + 1 == end)
        {
            if (Np->sign_wrote == TRUE && IS_BRACKET(Np->Num))
            {
                *Np->inout_p = Np->sign == '+' ? ' ' : '>';
                ++Np->inout_p;
            }
            else if (IS_LSIGN(Np->Num) && Np->Num->lsign == NUM_LSIGN_POST)
            {
                if (Np->sign == '-')
                    strcpy(Np->inout_p, Np->L_negative_sign);
                else
                    strcpy(Np->inout_p, Np->L_positive_sign);
                Np->inout_p += strlen(Np->inout_p);
            }
        }
    }

    ++Np->num_curr;
}

static void NUM_prepare_locale ( NUMProc Np  )  [static]

Definition at line 3936 of file formatting.c.

References NUMProc::decimal, IS_LDECIMAL, NUMProc::L_currency_symbol, NUMProc::L_negative_sign, NUMProc::L_positive_sign, NUMProc::L_thousands_sep, NUMDesc::need_locale, NUMProc::Num, and PGLC_localeconv().

Referenced by NUM_processor().

{
    if (Np->Num->need_locale)
    {
        struct lconv *lconv;

        /*
         * Get locales
         */
        lconv = PGLC_localeconv();

        /*
         * Positive / Negative number sign
         */
        if (lconv->negative_sign && *lconv->negative_sign)
            Np->L_negative_sign = lconv->negative_sign;
        else
            Np->L_negative_sign = "-";

        if (lconv->positive_sign && *lconv->positive_sign)
            Np->L_positive_sign = lconv->positive_sign;
        else
            Np->L_positive_sign = "+";

        /*
         * Number decimal point
         */
        if (lconv->decimal_point && *lconv->decimal_point)
            Np->decimal = lconv->decimal_point;

        else
            Np->decimal = ".";

        if (!IS_LDECIMAL(Np->Num))
            Np->decimal = ".";

        /*
         * Number thousands separator
         *
         * Some locales (e.g. broken glibc pt_BR), have a comma for decimal,
         * but "" for thousands_sep, so we set the thousands_sep too.
         * http://archives.postgresql.org/pgsql-hackers/2007-11/msg00772.php
         */
        if (lconv->thousands_sep && *lconv->thousands_sep)
            Np->L_thousands_sep = lconv->thousands_sep;
        /* Make sure thousands separator doesn't match decimal point symbol. */
        else if (strcmp(Np->decimal, ",") !=0)
            Np->L_thousands_sep = ",";
        else
            Np->L_thousands_sep = ".";

        /*
         * Currency symbol
         */
        if (lconv->currency_symbol && *lconv->currency_symbol)
            Np->L_currency_symbol = lconv->currency_symbol;
        else
            Np->L_currency_symbol = " ";
    }
    else
    {
        /*
         * Default values
         */
        Np->L_negative_sign = "-";
        Np->L_positive_sign = "+";
        Np->decimal = ".";

        Np->L_thousands_sep = ",";
        Np->L_currency_symbol = " ";
    }
}

static char * NUM_processor ( FormatNode node,
NUMDesc Num,
char *  inout,
char *  number,
int  plen,
int  sign,
bool  is_to_char,
Oid  collid 
) [static]

Definition at line 4465 of file formatting.c.

References asc_tolower_z(), FormatNode::character, elog, ereport, errcode(), errmsg(), ERROR, FALSE, NUMDesc::flag, get_last_relevant_decnum(), get_th(), KeyWord::id, NUMProc::inout, NUMProc::inout_p, IS_BRACKET, IS_DECIMAL, IS_EEEE, IS_FILLMODE, IS_LSIGN, IS_MINUS, IS_PLUS, IS_ROMAN, NUMProc::is_to_char, IS_ZERO, FormatNode::key, NUMProc::L_currency_symbol, NUMProc::L_thousands_sep, NUMProc::last_relevant, NUMDesc::lsign, MemSet, NODE_TYPE_ACTION, NODE_TYPE_END, NUMProc::Num, NUM_0, NUM_9, NUM_COMMA, NUMProc::num_count, NUMProc::num_curr, NUM_D, NUM_DEC, NUM_G, NUMProc::num_in, NUM_L, NUM_LSIGN_PRE, NUM_MI, NUM_numpart_from_char(), NUM_numpart_to_char(), NUM_PL, NUMProc::num_pre, NUM_prepare_locale(), NUM_rn, NUM_RN, NUM_SG, NUM_TH, NUM_th, NUMProc::number, NUMProc::number_p, NUMDesc::post, NUMDesc::pre, NUMDesc::pre_lsign_num, NUMProc::read_dec, NUMProc::read_post, NUMProc::read_pre, NUMProc::sign, NUMProc::sign_wrote, TH_LOWER, TH_UPPER, FormatNode::type, NUMDesc::zero_end, and NUMDesc::zero_start.

Referenced by numeric_to_number().

{
    FormatNode *n;
    NUMProc     _Np,
               *Np = &_Np;

    MemSet(Np, 0, sizeof(NUMProc));

    Np->Num = Num;
    Np->is_to_char = is_to_char;
    Np->number = number;
    Np->inout = inout;
    Np->last_relevant = NULL;
    Np->read_post = 0;
    Np->read_pre = 0;
    Np->read_dec = FALSE;

    if (Np->Num->zero_start)
        --Np->Num->zero_start;

    if (IS_EEEE(Np->Num))
    {
        if (!Np->is_to_char)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("\"EEEE\" not supported for input")));
        return strcpy(inout, number);
    }

    /*
     * Roman correction
     */
    if (IS_ROMAN(Np->Num))
    {
        if (!Np->is_to_char)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("\"RN\" not supported for input")));

        Np->Num->lsign = Np->Num->pre_lsign_num = Np->Num->post =
            Np->Num->pre = Np->num_pre = Np->sign = 0;

        if (IS_FILLMODE(Np->Num))
        {
            Np->Num->flag = 0;
            Np->Num->flag |= NUM_F_FILLMODE;
        }
        else
            Np->Num->flag = 0;
        Np->Num->flag |= NUM_F_ROMAN;
    }

    /*
     * Sign
     */
    if (is_to_char)
    {
        Np->sign = sign;

        /* MI/PL/SG - write sign itself and not in number */
        if (IS_PLUS(Np->Num) || IS_MINUS(Np->Num))
        {
            if (IS_PLUS(Np->Num) && IS_MINUS(Np->Num) == FALSE)
                Np->sign_wrote = FALSE; /* need sign */
            else
                Np->sign_wrote = TRUE;  /* needn't sign */
        }
        else
        {
            if (Np->sign != '-')
            {
                if (IS_BRACKET(Np->Num) && IS_FILLMODE(Np->Num))
                    Np->Num->flag &= ~NUM_F_BRACKET;
                if (IS_MINUS(Np->Num))
                    Np->Num->flag &= ~NUM_F_MINUS;
            }
            else if (Np->sign != '+' && IS_PLUS(Np->Num))
                Np->Num->flag &= ~NUM_F_PLUS;

            if (Np->sign == '+' && IS_FILLMODE(Np->Num) && IS_LSIGN(Np->Num) == FALSE)
                Np->sign_wrote = TRUE;  /* needn't sign */
            else
                Np->sign_wrote = FALSE; /* need sign */

            if (Np->Num->lsign == NUM_LSIGN_PRE && Np->Num->pre == Np->Num->pre_lsign_num)
                Np->Num->lsign = NUM_LSIGN_POST;
        }
    }
    else
        Np->sign = FALSE;

    /*
     * Count
     */
    Np->num_count = Np->Num->post + Np->Num->pre - 1;

    if (is_to_char)
    {
        Np->num_pre = plen;

        if (IS_FILLMODE(Np->Num) && IS_DECIMAL(Np->Num))
        {
            Np->last_relevant = get_last_relevant_decnum(Np->number);

            /*
             * If any '0' specifiers are present, make sure we don't strip
             * those digits.
             */
            if (Np->last_relevant && Np->Num->zero_end > Np->num_pre)
            {
                char       *last_zero;

                last_zero = Np->number + (Np->Num->zero_end - Np->num_pre);
                if (Np->last_relevant < last_zero)
                    Np->last_relevant = last_zero;
            }
        }

        if (Np->sign_wrote == FALSE && Np->num_pre == 0)
            ++Np->num_count;
    }
    else
    {
        Np->num_pre = 0;
        *Np->number = ' ';      /* sign space */
        *(Np->number + 1) = '\0';
    }

    Np->num_in = 0;
    Np->num_curr = 0;

#ifdef DEBUG_TO_FROM_CHAR
    elog(DEBUG_elog_output,
         "\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",
         Np->sign,
         Np->number,
         Np->Num->pre,
         Np->Num->post,
         Np->num_count,
         Np->num_pre,
         Np->sign_wrote ? "Yes" : "No",
         IS_ZERO(Np->Num) ? "Yes" : "No",
         Np->Num->zero_start,
         Np->Num->zero_end,
         Np->last_relevant ? Np->last_relevant : "<not set>",
         IS_BRACKET(Np->Num) ? "Yes" : "No",
         IS_PLUS(Np->Num) ? "Yes" : "No",
         IS_MINUS(Np->Num) ? "Yes" : "No",
         IS_FILLMODE(Np->Num) ? "Yes" : "No",
         IS_ROMAN(Np->Num) ? "Yes" : "No",
         IS_EEEE(Np->Num) ? "Yes" : "No"
        );
#endif

    /*
     * Locale
     */
    NUM_prepare_locale(Np);

    /*
     * Processor direct cycle
     */
    if (Np->is_to_char)
        Np->number_p = Np->number;
    else
        Np->number_p = Np->number + 1;  /* first char is space for sign */

    for (n = node, Np->inout_p = Np->inout; n->type != NODE_TYPE_END; n++)
    {
        if (!Np->is_to_char)
        {
            /*
             * Check non-string inout end
             */
            if (Np->inout_p >= Np->inout + plen)
                break;
        }

        /*
         * Format pictures actions
         */
        if (n->type == NODE_TYPE_ACTION)
        {
            /*
             * Create/reading digit/zero/blank/sing
             *
             * 'NUM_S' note: The locale sign is anchored to number and we
             * read/write it when we work with first or last number
             * (NUM_0/NUM_9). This is reason why NUM_S missing in follow
             * switch().
             */
            switch (n->key->id)
            {
                case NUM_9:
                case NUM_0:
                case NUM_DEC:
                case NUM_D:
                    if (Np->is_to_char)
                    {
                        NUM_numpart_to_char(Np, n->key->id);
                        continue;       /* for() */
                    }
                    else
                    {
                        NUM_numpart_from_char(Np, n->key->id, plen);
                        break;  /* switch() case: */
                    }

                case NUM_COMMA:
                    if (Np->is_to_char)
                    {
                        if (!Np->num_in)
                        {
                            if (IS_FILLMODE(Np->Num))
                                continue;
                            else
                                *Np->inout_p = ' ';
                        }
                        else
                            *Np->inout_p = ',';
                    }
                    else
                    {
                        if (!Np->num_in)
                        {
                            if (IS_FILLMODE(Np->Num))
                                continue;
                        }
                    }
                    break;

                case NUM_G:
                    if (Np->is_to_char)
                    {
                        if (!Np->num_in)
                        {
                            if (IS_FILLMODE(Np->Num))
                                continue;
                            else
                            {
                                int         x = strlen(Np->L_thousands_sep);

                                memset(Np->inout_p, ' ', x);
                                Np->inout_p += x - 1;
                            }
                        }
                        else
                        {
                            strcpy(Np->inout_p, Np->L_thousands_sep);
                            Np->inout_p += strlen(Np->inout_p) - 1;
                        }
                    }
                    else
                    {
                        if (!Np->num_in)
                        {
                            if (IS_FILLMODE(Np->Num))
                                continue;
                        }
                        Np->inout_p += strlen(Np->L_thousands_sep) - 1;
                    }
                    break;

                case NUM_L:
                    if (Np->is_to_char)
                    {
                        strcpy(Np->inout_p, Np->L_currency_symbol);
                        Np->inout_p += strlen(Np->inout_p) - 1;
                    }
                    else
                        Np->inout_p += strlen(Np->L_currency_symbol) - 1;
                    break;

                case NUM_RN:
                    if (IS_FILLMODE(Np->Num))
                    {
                        strcpy(Np->inout_p, Np->number_p);
                        Np->inout_p += strlen(Np->inout_p) - 1;
                    }
                    else
                    {
                        sprintf(Np->inout_p, "%15s", Np->number_p);
                        Np->inout_p += strlen(Np->inout_p) - 1;
                    }
                    break;

                case NUM_rn:
                    if (IS_FILLMODE(Np->Num))
                    {
                        strcpy(Np->inout_p, asc_tolower_z(Np->number_p));
                        Np->inout_p += strlen(Np->inout_p) - 1;
                    }
                    else
                    {
                        sprintf(Np->inout_p, "%15s", asc_tolower_z(Np->number_p));
                        Np->inout_p += strlen(Np->inout_p) - 1;
                    }
                    break;

                case NUM_th:
                    if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
                        Np->sign == '-' || IS_DECIMAL(Np->Num))
                        continue;

                    if (Np->is_to_char)
                        strcpy(Np->inout_p, get_th(Np->number, TH_LOWER));
                    Np->inout_p += 1;
                    break;

                case NUM_TH:
                    if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
                        Np->sign == '-' || IS_DECIMAL(Np->Num))
                        continue;

                    if (Np->is_to_char)
                        strcpy(Np->inout_p, get_th(Np->number, TH_UPPER));
                    Np->inout_p += 1;
                    break;

                case NUM_MI:
                    if (Np->is_to_char)
                    {
                        if (Np->sign == '-')
                            *Np->inout_p = '-';
                        else if (IS_FILLMODE(Np->Num))
                            continue;
                        else
                            *Np->inout_p = ' ';
                    }
                    else
                    {
                        if (*Np->inout_p == '-')
                            *Np->number = '-';
                    }
                    break;

                case NUM_PL:
                    if (Np->is_to_char)
                    {
                        if (Np->sign == '+')
                            *Np->inout_p = '+';
                        else if (IS_FILLMODE(Np->Num))
                            continue;
                        else
                            *Np->inout_p = ' ';
                    }
                    else
                    {
                        if (*Np->inout_p == '+')
                            *Np->number = '+';
                    }
                    break;

                case NUM_SG:
                    if (Np->is_to_char)
                        *Np->inout_p = Np->sign;

                    else
                    {
                        if (*Np->inout_p == '-')
                            *Np->number = '-';
                        else if (*Np->inout_p == '+')
                            *Np->number = '+';
                    }
                    break;


                default:
                    continue;
                    break;
            }
        }
        else
        {
            /*
             * Remove to output char from input in TO_CHAR
             */
            if (Np->is_to_char)
                *Np->inout_p = n->character;
        }
        Np->inout_p++;
    }

    if (Np->is_to_char)
    {
        *Np->inout_p = '\0';
        return Np->inout;
    }
    else
    {
        if (*(Np->number_p - 1) == '.')
            *(Np->number_p - 1) = '\0';
        else
            *Np->number_p = '\0';

        /*
         * Correction - precision of dec. number
         */
        Np->Num->post = Np->read_post;

#ifdef DEBUG_TO_FROM_CHAR
        elog(DEBUG_elog_output, "TO_NUMBER (number): '%s'", Np->number);
#endif
        return Np->number;
    }
}

static void NUMDesc_prepare ( NUMDesc num,
FormatNode n 
) [static]

Definition at line 1046 of file formatting.c.

References ereport, errcode(), errdetail(), errmsg(), ERROR, NUMDesc::flag, KeyWord::id, IS_BLANK, IS_BRACKET, IS_DECIMAL, IS_EEEE, IS_FILLMODE, IS_LSIGN, IS_MINUS, IS_MULTI, IS_PLUS, IS_ROMAN, IS_ZERO, FormatNode::key, NUMDesc::lsign, NUMDesc::multi, NUMDesc::need_locale, NODE_TYPE_ACTION, NUM_0, NUM_9, NUM_B, NUM_cache_remove(), NUM_D, NUM_DEC, NUM_E, NUM_FM, NUM_G, NUM_L, NUM_LSIGN_NONE, NUM_MI, NUM_PL, NUM_PR, NUM_RN, NUM_rn, NUM_S, NUM_SG, NUM_V, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, NUMDesc::post, NUMDesc::pre, NUMDesc::pre_lsign_num, FormatNode::type, NUMDesc::zero_end, and NUMDesc::zero_start.

Referenced by parse_format().

{

    if (n->type != NODE_TYPE_ACTION)
        return;

    /*
     * In case of an error, we need to remove the numeric from the cache.  Use
     * a PG_TRY block to ensure that this happens.
     */
    PG_TRY();
    {
        if (IS_EEEE(num) && n->key->id != NUM_E)
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("\"EEEE\" must be the last pattern used")));

        switch (n->key->id)
        {
            case NUM_9:
                if (IS_BRACKET(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("\"9\" must be ahead of \"PR\"")));
                if (IS_MULTI(num))
                {
                    ++num->multi;
                    break;
                }
                if (IS_DECIMAL(num))
                    ++num->post;
                else
                    ++num->pre;
                break;

            case NUM_0:
                if (IS_BRACKET(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("\"0\" must be ahead of \"PR\"")));
                if (!IS_ZERO(num) && !IS_DECIMAL(num))
                {
                    num->flag |= NUM_F_ZERO;
                    num->zero_start = num->pre + 1;
                }
                if (!IS_DECIMAL(num))
                    ++num->pre;
                else
                    ++num->post;

                num->zero_end = num->pre + num->post;
                break;

            case NUM_B:
                if (num->pre == 0 && num->post == 0 && (!IS_ZERO(num)))
                    num->flag |= NUM_F_BLANK;
                break;

            case NUM_D:
                num->flag |= NUM_F_LDECIMAL;
                num->need_locale = TRUE;
                /* FALLTHROUGH */
            case NUM_DEC:
                if (IS_DECIMAL(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("multiple decimal points")));
                if (IS_MULTI(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("cannot use \"V\" and decimal point together")));
                num->flag |= NUM_F_DECIMAL;
                break;

            case NUM_FM:
                num->flag |= NUM_F_FILLMODE;
                break;

            case NUM_S:
                if (IS_LSIGN(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("cannot use \"S\" twice")));
                if (IS_PLUS(num) || IS_MINUS(num) || IS_BRACKET(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("cannot use \"S\" and \"PL\"/\"MI\"/\"SG\"/\"PR\" together")));
                if (!IS_DECIMAL(num))
                {
                    num->lsign = NUM_LSIGN_PRE;
                    num->pre_lsign_num = num->pre;
                    num->need_locale = TRUE;
                    num->flag |= NUM_F_LSIGN;
                }
                else if (num->lsign == NUM_LSIGN_NONE)
                {
                    num->lsign = NUM_LSIGN_POST;
                    num->need_locale = TRUE;
                    num->flag |= NUM_F_LSIGN;
                }
                break;

            case NUM_MI:
                if (IS_LSIGN(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("cannot use \"S\" and \"MI\" together")));
                num->flag |= NUM_F_MINUS;
                if (IS_DECIMAL(num))
                    num->flag |= NUM_F_MINUS_POST;
                break;

            case NUM_PL:
                if (IS_LSIGN(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("cannot use \"S\" and \"PL\" together")));
                num->flag |= NUM_F_PLUS;
                if (IS_DECIMAL(num))
                    num->flag |= NUM_F_PLUS_POST;
                break;

            case NUM_SG:
                if (IS_LSIGN(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("cannot use \"S\" and \"SG\" together")));
                num->flag |= NUM_F_MINUS;
                num->flag |= NUM_F_PLUS;
                break;

            case NUM_PR:
                if (IS_LSIGN(num) || IS_PLUS(num) || IS_MINUS(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("cannot use \"PR\" and \"S\"/\"PL\"/\"MI\"/\"SG\" together")));
                num->flag |= NUM_F_BRACKET;
                break;

            case NUM_rn:
            case NUM_RN:
                num->flag |= NUM_F_ROMAN;
                break;

            case NUM_L:
            case NUM_G:
                num->need_locale = TRUE;
                break;

            case NUM_V:
                if (IS_DECIMAL(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("cannot use \"V\" and decimal point together")));
                num->flag |= NUM_F_MULTI;
                break;

            case NUM_E:
                if (IS_EEEE(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("cannot use \"EEEE\" twice")));
                if (IS_BLANK(num) || IS_FILLMODE(num) || IS_LSIGN(num) ||
                    IS_BRACKET(num) || IS_MINUS(num) || IS_PLUS(num) ||
                    IS_ROMAN(num) || IS_MULTI(num))
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                       errmsg("\"EEEE\" is incompatible with other formats"),
                             errdetail("\"EEEE\" may only be used together with digit and decimal point patterns.")));
                num->flag |= NUM_F_EEEE;
                break;
        }
    }
    PG_CATCH();
    {
        NUM_cache_remove(last_NUMCacheEntry);
        PG_RE_THROW();
    }
    PG_END_TRY();


    return;
}

Datum numeric_to_char ( PG_FUNCTION_ARGS   ) 

Definition at line 4952 of file formatting.c.

References DatumGetCString, DatumGetInt32, DatumGetNumeric, DirectFunctionCall1, DirectFunctionCall2, fill_str(), format, Int32GetDatum, int4_numeric(), int_to_roman(), IS_EEEE, IS_MULTI, IS_ROMAN, NUMDesc::multi, numeric_int4(), numeric_mul(), numeric_out(), numeric_out_sci(), numeric_power(), numeric_round(), NumericGetDatum, palloc(), PG_GETARG_NUMERIC, PG_GETARG_TEXT_P, PG_RETURN_TEXT_P, NUMDesc::post, NUMDesc::pre, sign, val, and value.

{
    Numeric     value = PG_GETARG_NUMERIC(0);
    text       *fmt = PG_GETARG_TEXT_P(1);
    NUMDesc     Num;
    FormatNode *format;
    text       *result;
    bool        shouldFree;
    int         len = 0,
                plen = 0,
                sign = 0;
    char       *numstr,
               *orgnum,
               *p;
    Numeric     x;

    NUM_TOCHAR_prepare;

    /*
     * On DateType depend part (numeric)
     */
    if (IS_ROMAN(&Num))
    {
        x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
                                                NumericGetDatum(value),
                                                Int32GetDatum(0)));
        numstr = orgnum =
            int_to_roman(DatumGetInt32(DirectFunctionCall1(numeric_int4,
                                                       NumericGetDatum(x))));
    }
    else if (IS_EEEE(&Num))
    {
        orgnum = numeric_out_sci(value, Num.post);

        /*
         * numeric_out_sci() does not emit a sign for positive numbers.  We
         * need to add a space in this case so that positive and negative
         * numbers are aligned.  We also have to do the right thing for NaN.
         */
        if (strcmp(orgnum, "NaN") == 0)
        {
            /*
             * Allow 6 characters for the leading sign, the decimal point,
             * "e", the exponent's sign and two exponent digits.
             */
            numstr = (char *) palloc(Num.pre + Num.post + 7);
            fill_str(numstr, '#', Num.pre + Num.post + 6);
            *numstr = ' ';
            *(numstr + Num.pre + 1) = '.';
        }
        else if (*orgnum != '-')
        {
            numstr = (char *) palloc(strlen(orgnum) + 2);
            *numstr = ' ';
            strcpy(numstr + 1, orgnum);
            len = strlen(numstr);
        }
        else
        {
            numstr = orgnum;
            len = strlen(orgnum);
        }
    }
    else
    {
        Numeric     val = value;

        if (IS_MULTI(&Num))
        {
            Numeric     a = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
                                                         Int32GetDatum(10)));
            Numeric     b = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
                                                  Int32GetDatum(Num.multi)));

            x = DatumGetNumeric(DirectFunctionCall2(numeric_power,
                                                    NumericGetDatum(a),
                                                    NumericGetDatum(b)));
            val = DatumGetNumeric(DirectFunctionCall2(numeric_mul,
                                                      NumericGetDatum(value),
                                                      NumericGetDatum(x)));
            Num.pre += Num.multi;
        }

        x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
                                                NumericGetDatum(val),
                                                Int32GetDatum(Num.post)));
        orgnum = DatumGetCString(DirectFunctionCall1(numeric_out,
                                                     NumericGetDatum(x)));

        if (*orgnum == '-')
        {
            sign = '-';
            numstr = orgnum + 1;
        }
        else
        {
            sign = '+';
            numstr = orgnum;
        }
        if ((p = strchr(numstr, '.')))
            len = p - numstr;
        else
            len = strlen(numstr);

        if (Num.pre > len)
            plen = Num.pre - len;
        else if (len > Num.pre)
        {
            numstr = (char *) palloc(Num.pre + Num.post + 2);
            fill_str(numstr, '#', Num.pre + Num.post + 1);
            *(numstr + Num.pre) = '.';
        }
    }

Datum numeric_to_number ( PG_FUNCTION_ARGS   ) 

Definition at line 4908 of file formatting.c.

References CStringGetDatum, DirectFunctionCall3, format, Int32GetDatum, InvalidOid, Max, NUM_cache(), NUM_MAX_ITEM_SIZ, NUM_processor(), numeric_in(), ObjectIdGetDatum, palloc(), pfree(), PG_GET_COLLATION, PG_GETARG_TEXT_P, PG_RETURN_NULL, NUMDesc::post, NUMDesc::pre, scale, value, VARDATA, VARHDRSZ, and VARSIZE.

{
    text       *value = PG_GETARG_TEXT_P(0);
    text       *fmt = PG_GETARG_TEXT_P(1);
    NUMDesc     Num;
    Datum       result;
    FormatNode *format;
    char       *numstr;
    bool        shouldFree;
    int         len = 0;
    int         scale,
                precision;

    len = VARSIZE(fmt) - VARHDRSZ;

    if (len <= 0 || len >= INT_MAX / NUM_MAX_ITEM_SIZ)
        PG_RETURN_NULL();

    format = NUM_cache(len, &Num, fmt, &shouldFree);

    numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1);

    NUM_processor(format, &Num, VARDATA(value), numstr,
                  VARSIZE(value) - VARHDRSZ, 0, false, PG_GET_COLLATION());

    scale = Num.post;
    precision = Max(0, Num.pre) + scale;

    if (shouldFree)
        pfree(format);

    result = DirectFunctionCall3(numeric_in,
                                 CStringGetDatum(numstr),
                                 ObjectIdGetDatum(InvalidOid),

static void parse_format ( FormatNode node,
char *  str,
const KeyWord kw,
KeySuffix suf,
const int *  index,
int  ver,
NUMDesc Num 
) [static]

Definition at line 1238 of file formatting.c.

References FormatNode::character, DCH_TYPE, elog, KeySuffix::id, index_seq_search(), FormatNode::key, KeyWord::len, KeySuffix::len, NODE_TYPE_ACTION, NULL, NUM_TYPE, NUMDesc_prepare(), suff_search(), FormatNode::suffix, SUFFTYPE_POSTFIX, SUFFTYPE_PREFIX, and FormatNode::type.

Referenced by datetime_to_char_body(), do_to_timestamp(), and NUM_cache().

{
    KeySuffix  *s;
    FormatNode *n;
    int         node_set = 0,
                suffix,
                last = 0;

#ifdef DEBUG_TO_FROM_CHAR
    elog(DEBUG_elog_output, "to_char/number(): run parser");
#endif

    n = node;

    while (*str)
    {
        suffix = 0;

        /*
         * Prefix
         */
        if (ver == DCH_TYPE && (s = suff_search(str, suf, SUFFTYPE_PREFIX)) != NULL)
        {
            suffix |= s->id;
            if (s->len)
                str += s->len;
        }

        /*
         * Keyword
         */
        if (*str && (n->key = index_seq_search(str, kw, index)) != NULL)
        {
            n->type = NODE_TYPE_ACTION;
            n->suffix = 0;
            node_set = 1;
            if (n->key->len)
                str += n->key->len;

            /*
             * NUM version: Prepare global NUMDesc struct
             */
            if (ver == NUM_TYPE)
                NUMDesc_prepare(Num, n);

            /*
             * Postfix
             */
            if (ver == DCH_TYPE && *str && (s = suff_search(str, suf, SUFFTYPE_POSTFIX)) != NULL)
            {
                suffix |= s->id;
                if (s->len)
                    str += s->len;
            }
        }
        else if (*str)
        {
            /*
             * Special characters '\' and '"'
             */
            if (*str == '"' && last != '\\')
            {
                int         x = 0;

                while (*(++str))
                {
                    if (*str == '"' && x != '\\')
                    {
                        str++;
                        break;
                    }
                    else if (*str == '\\' && x != '\\')
                    {
                        x = '\\';
                        continue;
                    }
                    n->type = NODE_TYPE_CHAR;
                    n->character = *str;
                    n->key = NULL;
                    n->suffix = 0;
                    ++n;
                    x = *str;
                }
                node_set = 0;
                suffix = 0;
                last = 0;
            }
            else if (*str && *str == '\\' && last != '\\' && *(str + 1) == '"')
            {
                last = *str;
                str++;
            }
            else if (*str)
            {
                n->type = NODE_TYPE_CHAR;
                n->character = *str;
                n->key = NULL;
                node_set = 1;
                last = 0;
                str++;
            }
        }

        /* end */
        if (node_set)
        {
            if (n->type == NODE_TYPE_ACTION)
                n->suffix = suffix;
            ++n;

            n->suffix = 0;
            node_set = 0;
        }
    }

    n->type = NODE_TYPE_END;
    n->suffix = 0;
    return;
}

static int seq_search ( char *  name,
char **  array,
int  type,
int  max,
int *  len 
) [static]

Definition at line 2270 of file formatting.c.

References ALL_LOWER, ALL_UPPER, elog, i, ONE_UPPER, pg_tolower(), and pg_toupper().

Referenced by from_char_seq_search().

{
    char       *p,
               *n,
              **a;
    int         last,
                i;

    *len = 0;

    if (!*name)
        return -1;

    /* set first char */
    if (type == ONE_UPPER || type == ALL_UPPER)
        *name = pg_toupper((unsigned char) *name);
    else if (type == ALL_LOWER)
        *name = pg_tolower((unsigned char) *name);

    for (last = 0, a = array; *a != NULL; a++)
    {
        /* comperate first chars */
        if (*name != **a)
            continue;

        for (i = 1, p = *a + 1, n = name + 1;; n++, p++, i++)
        {
            /* search fragment (max) only */
            if (max && i == max)
            {
                *len = i;
                return a - array;
            }
            /* full size */
            if (*p == '\0')
            {
                *len = i;
                return a - array;
            }
            /* Not found in array 'a' */
            if (*n == '\0')
                break;

            /*
             * Convert (but convert new chars only)
             */
            if (i > last)
            {
                if (type == ONE_UPPER || type == ALL_LOWER)
                    *n = pg_tolower((unsigned char) *n);
                else if (type == ALL_UPPER)
                    *n = pg_toupper((unsigned char) *n);
                last = i;
            }

#ifdef DEBUG_TO_FROM_CHAR
            elog(DEBUG_elog_output, "N: %c, P: %c, A: %s (%s)",
                 *n, *p, *a, name);
#endif
            if (*n != *p)
                break;
        }
    }

    return -1;
}

char* str_initcap ( const char *  buff,
size_t  nbytes,
Oid  collid 
)

Definition at line 1725 of file formatting.c.

References asc_initcap(), DEFAULT_COLLATION_OID, ereport, errcode(), errhint(), errmsg(), ERROR, isalnum_l, iswalnum_l, lc_ctype_is_c(), OidIsValid, palloc(), pfree(), pg_database_encoding_max_length(), pg_newlocale_from_collation(), pg_tolower(), pg_toupper(), pnstrdup(), tolower_l, toupper_l, towlower_l, and towupper_l.

Referenced by initcap(), and str_initcap_z().

{
    char       *result;
    int         wasalnum = false;

    if (!buff)
        return NULL;

    /* C/POSIX collations use this path regardless of database encoding */
    if (lc_ctype_is_c(collid))
    {
        result = asc_initcap(buff, nbytes);
    }
#ifdef USE_WIDE_UPPER_LOWER
    else if (pg_database_encoding_max_length() > 1)
    {
        pg_locale_t mylocale = 0;
        wchar_t    *workspace;
        size_t      curr_char;
        size_t      result_size;

        if (collid != DEFAULT_COLLATION_OID)
        {
            if (!OidIsValid(collid))
            {
                /*
                 * This typically means that the parser could not resolve a
                 * conflict of implicit collations, so report it that way.
                 */
                ereport(ERROR,
                        (errcode(ERRCODE_INDETERMINATE_COLLATION),
                         errmsg("could not determine which collation to use for initcap() function"),
                         errhint("Use the COLLATE clause to set the collation explicitly.")));
            }
            mylocale = pg_newlocale_from_collation(collid);
        }

        /* Overflow paranoia */
        if ((nbytes + 1) > (INT_MAX / sizeof(wchar_t)))
            ereport(ERROR,
                    (errcode(ERRCODE_OUT_OF_MEMORY),
                     errmsg("out of memory")));

        /* Output workspace cannot have more codes than input bytes */
        workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));

        char2wchar(workspace, nbytes + 1, buff, nbytes, mylocale);

        for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
        {
#ifdef HAVE_LOCALE_T
            if (mylocale)
            {
                if (wasalnum)
                    workspace[curr_char] = towlower_l(workspace[curr_char], mylocale);
                else
                    workspace[curr_char] = towupper_l(workspace[curr_char], mylocale);
                wasalnum = iswalnum_l(workspace[curr_char], mylocale);
            }
            else
#endif
            {
                if (wasalnum)
                    workspace[curr_char] = towlower(workspace[curr_char]);
                else
                    workspace[curr_char] = towupper(workspace[curr_char]);
                wasalnum = iswalnum(workspace[curr_char]);
            }
        }

        /* Make result large enough; case change might change number of bytes */
        result_size = curr_char * pg_database_encoding_max_length() + 1;
        result = palloc(result_size);

        wchar2char(result, workspace, result_size, mylocale);
        pfree(workspace);
    }
#endif   /* USE_WIDE_UPPER_LOWER */
    else
    {
#ifdef HAVE_LOCALE_T
        pg_locale_t mylocale = 0;
#endif
        char       *p;

        if (collid != DEFAULT_COLLATION_OID)
        {
            if (!OidIsValid(collid))
            {
                /*
                 * This typically means that the parser could not resolve a
                 * conflict of implicit collations, so report it that way.
                 */
                ereport(ERROR,
                        (errcode(ERRCODE_INDETERMINATE_COLLATION),
                         errmsg("could not determine which collation to use for initcap() function"),
                         errhint("Use the COLLATE clause to set the collation explicitly.")));
            }
#ifdef HAVE_LOCALE_T
            mylocale = pg_newlocale_from_collation(collid);
#endif
        }

        result = pnstrdup(buff, nbytes);

        /*
         * Note: we assume that toupper_l()/tolower_l() will not be so broken
         * as to need guard tests.  When using the default collation, we apply
         * the traditional Postgres behavior that forces ASCII-style treatment
         * of I/i, but in non-default collations you get exactly what the
         * collation says.
         */
        for (p = result; *p; p++)
        {
#ifdef HAVE_LOCALE_T
            if (mylocale)
            {
                if (wasalnum)
                    *p = tolower_l((unsigned char) *p, mylocale);
                else
                    *p = toupper_l((unsigned char) *p, mylocale);
                wasalnum = isalnum_l((unsigned char) *p, mylocale);
            }
            else
#endif
            {
                if (wasalnum)
                    *p = pg_tolower((unsigned char) *p);
                else
                    *p = pg_toupper((unsigned char) *p);
                wasalnum = isalnum((unsigned char) *p);
            }
        }
    }

    return result;
}

static char* str_initcap_z ( const char *  buff,
Oid  collid 
) [static]

Definition at line 1959 of file formatting.c.

References str_initcap().

Referenced by DCH_to_char().

{
    return str_initcap(buff, strlen(buff), collid);
}

static char * str_numth ( char *  dest,
char *  num,
int  type 
) [static]

Definition at line 1450 of file formatting.c.

References get_th().

Referenced by DCH_to_char().

{
    if (dest != num)
        strcpy(dest, num);
    strcat(dest, get_th(num, type));
    return dest;
}

char* str_tolower ( const char *  buff,
size_t  nbytes,
Oid  collid 
)

Definition at line 1485 of file formatting.c.

References asc_tolower(), DEFAULT_COLLATION_OID, ereport, errcode(), errhint(), errmsg(), ERROR, lc_ctype_is_c(), OidIsValid, palloc(), pfree(), pg_database_encoding_max_length(), pg_newlocale_from_collation(), pg_tolower(), pnstrdup(), tolower_l, and towlower_l.

Referenced by citext_eq(), citext_hash(), citext_ne(), citextcmp(), lower(), ltree_strncasecmp(), and str_tolower_z().

{
    char       *result;

    if (!buff)
        return NULL;

    /* C/POSIX collations use this path regardless of database encoding */
    if (lc_ctype_is_c(collid))
    {
        result = asc_tolower(buff, nbytes);
    }
#ifdef USE_WIDE_UPPER_LOWER
    else if (pg_database_encoding_max_length() > 1)
    {
        pg_locale_t mylocale = 0;
        wchar_t    *workspace;
        size_t      curr_char;
        size_t      result_size;

        if (collid != DEFAULT_COLLATION_OID)
        {
            if (!OidIsValid(collid))
            {
                /*
                 * This typically means that the parser could not resolve a
                 * conflict of implicit collations, so report it that way.
                 */
                ereport(ERROR,
                        (errcode(ERRCODE_INDETERMINATE_COLLATION),
                         errmsg("could not determine which collation to use for lower() function"),
                         errhint("Use the COLLATE clause to set the collation explicitly.")));
            }
            mylocale = pg_newlocale_from_collation(collid);
        }

        /* Overflow paranoia */
        if ((nbytes + 1) > (INT_MAX / sizeof(wchar_t)))
            ereport(ERROR,
                    (errcode(ERRCODE_OUT_OF_MEMORY),
                     errmsg("out of memory")));

        /* Output workspace cannot have more codes than input bytes */
        workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));

        char2wchar(workspace, nbytes + 1, buff, nbytes, mylocale);

        for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
        {
#ifdef HAVE_LOCALE_T
            if (mylocale)
                workspace[curr_char] = towlower_l(workspace[curr_char], mylocale);
            else
#endif
                workspace[curr_char] = towlower(workspace[curr_char]);
        }

        /* Make result large enough; case change might change number of bytes */
        result_size = curr_char * pg_database_encoding_max_length() + 1;
        result = palloc(result_size);

        wchar2char(result, workspace, result_size, mylocale);
        pfree(workspace);
    }
#endif   /* USE_WIDE_UPPER_LOWER */
    else
    {
#ifdef HAVE_LOCALE_T
        pg_locale_t mylocale = 0;
#endif
        char       *p;

        if (collid != DEFAULT_COLLATION_OID)
        {
            if (!OidIsValid(collid))
            {
                /*
                 * This typically means that the parser could not resolve a
                 * conflict of implicit collations, so report it that way.
                 */
                ereport(ERROR,
                        (errcode(ERRCODE_INDETERMINATE_COLLATION),
                         errmsg("could not determine which collation to use for lower() function"),
                         errhint("Use the COLLATE clause to set the collation explicitly.")));
            }
#ifdef HAVE_LOCALE_T
            mylocale = pg_newlocale_from_collation(collid);
#endif
        }

        result = pnstrdup(buff, nbytes);

        /*
         * Note: we assume that tolower_l() will not be so broken as to need
         * an isupper_l() guard test.  When using the default collation, we
         * apply the traditional Postgres behavior that forces ASCII-style
         * treatment of I/i, but in non-default collations you get exactly
         * what the collation says.
         */
        for (p = result; *p; p++)
        {
#ifdef HAVE_LOCALE_T
            if (mylocale)
                *p = tolower_l((unsigned char) *p, mylocale);
            else
#endif
                *p = pg_tolower((unsigned char) *p);
        }
    }

    return result;
}

static char* str_tolower_z ( const char *  buff,
Oid  collid 
) [static]

Definition at line 1947 of file formatting.c.

References str_tolower().

Referenced by DCH_to_char().

{
    return str_tolower(buff, strlen(buff), collid);
}

char* str_toupper ( const char *  buff,
size_t  nbytes,
Oid  collid 
)

Definition at line 1605 of file formatting.c.

References asc_toupper(), DEFAULT_COLLATION_OID, ereport, errcode(), errhint(), errmsg(), ERROR, lc_ctype_is_c(), OidIsValid, palloc(), pfree(), pg_database_encoding_max_length(), pg_newlocale_from_collation(), pg_toupper(), pnstrdup(), toupper_l, and towupper_l.

Referenced by str_toupper_z(), and upper().

{
    char       *result;

    if (!buff)
        return NULL;

    /* C/POSIX collations use this path regardless of database encoding */
    if (lc_ctype_is_c(collid))
    {
        result = asc_toupper(buff, nbytes);
    }
#ifdef USE_WIDE_UPPER_LOWER
    else if (pg_database_encoding_max_length() > 1)
    {
        pg_locale_t mylocale = 0;
        wchar_t    *workspace;
        size_t      curr_char;
        size_t      result_size;

        if (collid != DEFAULT_COLLATION_OID)
        {
            if (!OidIsValid(collid))
            {
                /*
                 * This typically means that the parser could not resolve a
                 * conflict of implicit collations, so report it that way.
                 */
                ereport(ERROR,
                        (errcode(ERRCODE_INDETERMINATE_COLLATION),
                         errmsg("could not determine which collation to use for upper() function"),
                         errhint("Use the COLLATE clause to set the collation explicitly.")));
            }
            mylocale = pg_newlocale_from_collation(collid);
        }

        /* Overflow paranoia */
        if ((nbytes + 1) > (INT_MAX / sizeof(wchar_t)))
            ereport(ERROR,
                    (errcode(ERRCODE_OUT_OF_MEMORY),
                     errmsg("out of memory")));

        /* Output workspace cannot have more codes than input bytes */
        workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));

        char2wchar(workspace, nbytes + 1, buff, nbytes, mylocale);

        for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
        {
#ifdef HAVE_LOCALE_T
            if (mylocale)
                workspace[curr_char] = towupper_l(workspace[curr_char], mylocale);
            else
#endif
                workspace[curr_char] = towupper(workspace[curr_char]);
        }

        /* Make result large enough; case change might change number of bytes */
        result_size = curr_char * pg_database_encoding_max_length() + 1;
        result = palloc(result_size);

        wchar2char(result, workspace, result_size, mylocale);
        pfree(workspace);
    }
#endif   /* USE_WIDE_UPPER_LOWER */
    else
    {
#ifdef HAVE_LOCALE_T
        pg_locale_t mylocale = 0;
#endif
        char       *p;

        if (collid != DEFAULT_COLLATION_OID)
        {
            if (!OidIsValid(collid))
            {
                /*
                 * This typically means that the parser could not resolve a
                 * conflict of implicit collations, so report it that way.
                 */
                ereport(ERROR,
                        (errcode(ERRCODE_INDETERMINATE_COLLATION),
                         errmsg("could not determine which collation to use for upper() function"),
                         errhint("Use the COLLATE clause to set the collation explicitly.")));
            }
#ifdef HAVE_LOCALE_T
            mylocale = pg_newlocale_from_collation(collid);
#endif
        }

        result = pnstrdup(buff, nbytes);

        /*
         * Note: we assume that toupper_l() will not be so broken as to need
         * an islower_l() guard test.  When using the default collation, we
         * apply the traditional Postgres behavior that forces ASCII-style
         * treatment of I/i, but in non-default collations you get exactly
         * what the collation says.
         */
        for (p = result; *p; p++)
        {
#ifdef HAVE_LOCALE_T
            if (mylocale)
                *p = toupper_l((unsigned char) *p, mylocale);
            else
#endif
                *p = pg_toupper((unsigned char) *p);
        }
    }

    return result;
}

static char* str_toupper_z ( const char *  buff,
Oid  collid 
) [static]

Definition at line 1953 of file formatting.c.

References str_toupper().

Referenced by DCH_to_char().

{
    return str_toupper(buff, strlen(buff), collid);
}

static int strdigits_len ( char *  str  )  [static]

Definition at line 2092 of file formatting.c.

References DCH_MAX_ITEM_SIZ, and strspace_len().

Referenced by DCH_from_char().

{
    char       *p = str;
    int         len;

    len = strspace_len(str);
    p += len;

    while (*p && isdigit((unsigned char) *p) && len <= DCH_MAX_ITEM_SIZ)
    {
        len++;
        p++;
    }
    return len;
}

static int strspace_len ( char *  str  )  [static]

Definition at line 2079 of file formatting.c.

Referenced by from_char_parse_int_len(), and strdigits_len().

{
    int         len = 0;

    while (*str && isspace((unsigned char) *str))
    {
        str++;
        len++;
    }
    return len;
}

static KeySuffix * suff_search ( char *  str,
KeySuffix suf,
int  type 
) [static]

Definition at line 1026 of file formatting.c.

References KeySuffix::len, KeySuffix::name, and KeySuffix::type.

Referenced by parse_format().

{
    KeySuffix  *s;

    for (s = suf; s->name != NULL; s++)
    {
        if (s->type != type)
            continue;

        if (strncmp(str, s->name, s->len) == 0)
            return s;
    }
    return NULL;
}

Datum timestamp_to_char ( PG_FUNCTION_ARGS   ) 

Definition at line 3258 of file formatting.c.

References date2j(), datetime_to_char_body(), ereport, errcode(), errmsg(), ERROR, NULL, PG_GET_COLLATION, PG_GETARG_TEXT_P, PG_GETARG_TIMESTAMP, PG_RETURN_NULL, PG_RETURN_TEXT_P, timestamp2tm(), TIMESTAMP_NOT_FINITE, tm, pg_tm::tm_mday, pg_tm::tm_mon, pg_tm::tm_wday, pg_tm::tm_yday, pg_tm::tm_year, tmtcFsec, tmtcTm, VARHDRSZ, VARSIZE, and ZERO_tmtc.

{
    Timestamp   dt = PG_GETARG_TIMESTAMP(0);
    text       *fmt = PG_GETARG_TEXT_P(1),
               *res;
    TmToChar    tmtc;
    struct pg_tm *tm;
    int         thisdate;

    if ((VARSIZE(fmt) - VARHDRSZ) <= 0 || TIMESTAMP_NOT_FINITE(dt))
        PG_RETURN_NULL();

    ZERO_tmtc(&tmtc);
    tm = tmtcTm(&tmtc);

    if (timestamp2tm(dt, NULL, tm, &tmtcFsec(&tmtc), NULL, NULL) != 0)
        ereport(ERROR,
                (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                 errmsg("timestamp out of range")));

    thisdate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
    tm->tm_wday = (thisdate + 1) % 7;
    tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1;

    if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION())))
        PG_RETURN_NULL();

    PG_RETURN_TEXT_P(res);
}

Datum timestamptz_to_char ( PG_FUNCTION_ARGS   ) 

Definition at line 3289 of file formatting.c.

References date2j(), datetime_to_char_body(), ereport, errcode(), errmsg(), ERROR, NULL, PG_GET_COLLATION, PG_GETARG_TEXT_P, PG_GETARG_TIMESTAMP, PG_RETURN_NULL, PG_RETURN_TEXT_P, timestamp2tm(), TIMESTAMP_NOT_FINITE, tm, pg_tm::tm_mday, pg_tm::tm_mon, pg_tm::tm_wday, pg_tm::tm_yday, pg_tm::tm_year, tmtcFsec, tmtcTm, tmtcTzn, VARHDRSZ, VARSIZE, and ZERO_tmtc.

{
    TimestampTz dt = PG_GETARG_TIMESTAMP(0);
    text       *fmt = PG_GETARG_TEXT_P(1),
               *res;
    TmToChar    tmtc;
    int         tz;
    struct pg_tm *tm;
    int         thisdate;

    if ((VARSIZE(fmt) - VARHDRSZ) <= 0 || TIMESTAMP_NOT_FINITE(dt))
        PG_RETURN_NULL();

    ZERO_tmtc(&tmtc);
    tm = tmtcTm(&tmtc);

    if (timestamp2tm(dt, &tz, tm, &tmtcFsec(&tmtc), &tmtcTzn(&tmtc), NULL) != 0)
        ereport(ERROR,
                (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                 errmsg("timestamp out of range")));

    thisdate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
    tm->tm_wday = (thisdate + 1) % 7;
    tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1;

    if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION())))
        PG_RETURN_NULL();

    PG_RETURN_TEXT_P(res);
}

Datum to_date ( PG_FUNCTION_ARGS   ) 

Definition at line 3387 of file formatting.c.

References date2j(), do_to_timestamp(), ereport, errcode(), errmsg(), ERROR, IS_VALID_JULIAN, PG_GETARG_TEXT_P, PG_RETURN_DATEADT, text_to_cstring(), pg_tm::tm_mday, pg_tm::tm_mon, and pg_tm::tm_year.

{
    text       *date_txt = PG_GETARG_TEXT_P(0);
    text       *fmt = PG_GETARG_TEXT_P(1);
    DateADT     result;
    struct pg_tm tm;
    fsec_t      fsec;

    do_to_timestamp(date_txt, fmt, &tm, &fsec);

    if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
        ereport(ERROR,
                (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                 errmsg("date out of range: \"%s\"",
                        text_to_cstring(date_txt))));

    result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;

    PG_RETURN_DATEADT(result);
}

Datum to_timestamp ( PG_FUNCTION_ARGS   ) 

Definition at line 3360 of file formatting.c.

References DetermineTimeZoneOffset(), do_to_timestamp(), ereport, errcode(), errmsg(), ERROR, PG_GETARG_TEXT_P, PG_RETURN_TIMESTAMP, session_timezone, and tm2timestamp().

{
    text       *date_txt = PG_GETARG_TEXT_P(0);
    text       *fmt = PG_GETARG_TEXT_P(1);
    Timestamp   result;
    int         tz;
    struct pg_tm tm;
    fsec_t      fsec;

    do_to_timestamp(date_txt, fmt, &tm, &fsec);

    tz = DetermineTimeZoneOffset(&tm, session_timezone);

    if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
        ereport(ERROR,
                (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                 errmsg("timestamp out of range")));

    PG_RETURN_TIMESTAMP(result);
}


Variable Documentation

char* adbc_strings[] = {ad_STR, bc_STR, AD_STR, BC_STR, NULL} [static]

Definition at line 229 of file formatting.c.

Referenced by DCH_from_char().

char* adbc_strings_long[] = {a_d_STR, b_c_STR, A_D_STR, B_C_STR, NULL} [static]

Definition at line 230 of file formatting.c.

Referenced by DCH_from_char().

char* ampm_strings[] = {am_STR, pm_STR, AM_STR, PM_STR, NULL} [static]

Definition at line 256 of file formatting.c.

Referenced by DCH_from_char().

char* ampm_strings_long[] = {a_m_STR, p_m_STR, A_M_STR, P_M_STR, NULL} [static]

Definition at line 257 of file formatting.c.

Referenced by DCH_from_char().

char * days[]

Definition at line 67 of file datetime.c.

char* days_short[] [static]
Initial value:
 {
    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
}

Definition at line 196 of file formatting.c.

Referenced by DCH_to_char().

const int DCH_index[KeyWord_INDEX_SIZE] [static]
Initial value:
 {

    

    -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, DCH_A_D, DCH_B_C, DCH_CC, DCH_DAY, -1,
    DCH_FX, -1, DCH_HH24, DCH_IDDD, DCH_J, -1, -1, DCH_MI, -1, -1,
    DCH_P_M, DCH_Q, DCH_RM, DCH_SSSS, DCH_TZ, DCH_US, -1, DCH_WW, -1, DCH_Y_YYY,
    -1, -1, -1, -1, -1, -1, -1, DCH_a_d, DCH_b_c, DCH_cc,
    DCH_day, -1, DCH_fx, -1, DCH_hh24, DCH_iddd, DCH_j, -1, -1, DCH_mi,
    -1, -1, DCH_p_m, DCH_q, DCH_rm, DCH_ssss, DCH_tz, DCH_us, -1, DCH_ww,
    -1, DCH_y_yyy, -1, -1, -1, -1

    
}

Definition at line 867 of file formatting.c.

Referenced by datetime_to_char_body(), and do_to_timestamp().

const KeyWord DCH_keywords[] [static]

Definition at line 714 of file formatting.c.

KeySuffix DCH_suff[] [static]
Initial value:
 {
    {"FM", 2, DCH_S_FM, SUFFTYPE_PREFIX},
    {"fm", 2, DCH_S_FM, SUFFTYPE_PREFIX},
    {"TM", 2, DCH_S_TM, SUFFTYPE_PREFIX},
    {"tm", 2, DCH_S_TM, SUFFTYPE_PREFIX},
    {"TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX},
    {"th", 2, DCH_S_th, SUFFTYPE_POSTFIX},
    {"SP", 2, DCH_S_SP, SUFFTYPE_POSTFIX},
    
    {NULL, 0, 0, 0}
}

Definition at line 528 of file formatting.c.

DCHCacheEntry DCHCache[DCH_CACHE_FIELDS+1] [static]

Definition at line 391 of file formatting.c.

int DCHCounter = 0 [static]

Definition at line 394 of file formatting.c.

Referenced by DCH_cache_getnew(), and DCH_cache_search().

Definition at line 401 of file formatting.c.

char* months[]

Definition at line 64 of file datetime.c.

char* months_full[] [static]
Initial value:
 {
    "January", "February", "March", "April", "May", "June", "July",
    "August", "September", "October", "November", "December", NULL
}

Definition at line 191 of file formatting.c.

Referenced by DCH_from_char(), and DCH_to_char().

int n_DCHCache = 0 [static]

Definition at line 393 of file formatting.c.

Referenced by DCH_cache_getnew(), and DCH_cache_search().

int n_NUMCache = 0 [static]

Definition at line 399 of file formatting.c.

Referenced by NUM_cache_getnew(), and NUM_cache_search().

const int NUM_index[KeyWord_INDEX_SIZE] [static]
Initial value:
 {

    

    -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, NUM_COMMA, -1, NUM_DEC, -1, NUM_0, -1,
    -1, -1, -1, -1, -1, -1, -1, NUM_9, -1, -1,
    -1, -1, -1, -1, -1, -1, NUM_B, NUM_C, NUM_D, NUM_E,
    NUM_FM, NUM_G, -1, -1, -1, -1, NUM_L, NUM_MI, -1, -1,
    NUM_PL, -1, NUM_RN, NUM_SG, NUM_TH, -1, NUM_V, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, NUM_b, NUM_c,
    NUM_d, NUM_e, NUM_fm, NUM_g, -1, -1, -1, -1, NUM_l, NUM_mi,
    -1, -1, NUM_pl, -1, NUM_rn, NUM_sg, NUM_th, -1, NUM_v, -1,
    -1, -1, -1, -1, -1, -1

    
}

Definition at line 891 of file formatting.c.

Referenced by NUM_cache().

const KeyWord NUM_keywords[] [static]

Definition at line 819 of file formatting.c.

NUMCacheEntry NUMCache[NUM_CACHE_FIELDS+1] [static]

Definition at line 397 of file formatting.c.

int NUMCounter = 0 [static]

Definition at line 400 of file formatting.c.

Referenced by NUM_cache_getnew(), and NUM_cache_search().

char* numTH[] = {"ST", "ND", "RD", "TH", NULL} [static]

Definition at line 283 of file formatting.c.

Referenced by get_th().

char* numth[] = {"st", "nd", "rd", "th", NULL} [static]

Definition at line 284 of file formatting.c.

Referenced by get_th().

char* rm1[] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", NULL} [static]

Definition at line 275 of file formatting.c.

Referenced by int_to_roman().

char* rm10[] = {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", NULL} [static]

Definition at line 276 of file formatting.c.

Referenced by int_to_roman().

char* rm100[] = {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", NULL} [static]

Definition at line 277 of file formatting.c.

Referenced by int_to_roman().

char* rm_months_lower[] [static]
Initial value:
{"xii", "xi", "x", "ix", "viii", "vii", "vi", "v", "iv", "iii", "ii", "i", NULL}

Definition at line 268 of file formatting.c.

Referenced by DCH_from_char(), and DCH_to_char().

char* rm_months_upper[] [static]
Initial value:
{"XII", "XI", "X", "IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I", NULL}

Definition at line 265 of file formatting.c.

Referenced by DCH_from_char(), and DCH_to_char().