Unfortunately, it does not apply cleanly to the current CVS tree, and I
can't figure out how to merge your changes. Current formatting.c
attached.
>
> On Wed, 18 Oct 2000, Tom Lane wrote:
>
> > Karel Zak <zakkr@zf.jcu.cz> writes:
> > > PS. 7.1 contains more changes for to_char/to_timestamp, this fix PM/AM
> > > only ....
> >
> > Sounds good, but you forgot to attach the patch ...
>
> Is it a problem? :-)
>
> Karel
Content-Description:
[ application/x-gzip is not supported, skipping... ]
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 853-3000
+ If your life is a hard drive, | 830 Blythe Avenue
+ Christ can be your backup. | Drexel Hill, Pennsylvania 19026
/* -----------------------------------------------------------------------
* formatting.c
*
* $Header: /home/projects/pgsql/cvsroot/pgsql/src/backend/utils/adt/formatting.c,v 1.22 2000/09/25 12:58:47 momjian
Exp$
*
*
* Portions Copyright (c) 1999-2000, PostgreSQL, Inc
*
*
* TO_CHAR(); TO_TIMESTAMP(); TO_DATE(); TO_NUMBER();
*
* The PostgreSQL routines for a timestamp/int/float/numeric formatting,
* inspire with Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines.
*
*
* Cache & Memory:
* Routines use (itself) internal cache for format pictures.
*
* The cache uses a static buffers and is persistent across transactions.
* If format-picture is bigger than cache buffer, parser is called always.
*
* NOTE for Number version:
* All in this version is implemented as keywords ( => not used
* suffixes), because a format picture is for *one* item (number)
* only. It not is as a timestamp version, where each keyword (can)
* has suffix.
*
* NOTE for Timestamp routines:
* In this module the POSIX 'struct tm' type is *not* used, but rather
* PgSQL type, which has tm_mon based on one (*non* zero) and
* year *not* based on 1900, but is used full year number.
* Module supports AD / BC / AM / PM.
*
* Supported types for to_char():
*
* Timestamp, Numeric, int4, int8, float4, float8
*
* Supported types for reverse conversion:
*
* Timestamp - to_timestamp()
* Date - to_date()
* Numeric - to_number()
*
*
* Karel Zak
*
* -----------------------------------------------------------------------
*/
/* ----------
* UnComment me for DEBUG
* ----------
*/
/***
#define DEBUG_TO_FROM_CHAR
#define DEBUG_elog_output NOTICE
***/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#include <unistd.h>
#include <locale.h>
#include <math.h>
#include <float.h>
#include "postgres.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
#include "utils/formatting.h"
#include "utils/int8.h"
#include "utils/pg_locale.h"
/* ----------
* Routines type
* ----------
*/
#define DCH_TYPE 1 /* DATE-TIME version */
#define NUM_TYPE 2 /* NUMBER version */
/* ----------
* KeyWord Index (ascii from position 32 (' ') to 126 (~))
* ----------
*/
#define KeyWord_INDEX_SIZE ('~' - ' ')
#define KeyWord_INDEX_FILTER(_c) ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
/* ----------
* Maximal length of one node
* ----------
*/
#define DCH_MAX_ITEM_SIZ 9 /* max julian day */
#define NUM_MAX_ITEM_SIZ 8 /* roman number (RN has 15 chars) */
/* ----------
* More is in float.c
* ----------
*/
#define MAXFLOATWIDTH 64
#define MAXDOUBLEWIDTH 128
/* ----------
* External (defined in PgSQL dt.c (timestamp utils))
* ----------
*/
extern char *months[], /* month abbreviation */
*days[]; /* full days */
/* ----------
* Format parser structs
* ----------
*/
typedef struct
{
char *name; /* suffix string */
int len, /* suffix length */
id, /* used in node->suffix */
type; /* prefix / postfix */
} KeySuffix;
typedef struct
{
char *name; /* keyword */
/* action for keyword */
int len, /* keyword length */
(*action) (),
id; /* keyword id */
} KeyWord;
typedef struct
{
int type; /* node type */
KeyWord *key; /* if node type is KEYWORD */
int character, /* if node type is CHAR */
suffix; /* keyword suffix */
} FormatNode;
#define NODE_TYPE_END 1
#define NODE_TYPE_ACTION 2
#define NODE_TYPE_CHAR 3
#define SUFFTYPE_PREFIX 1
#define SUFFTYPE_POSTFIX 2
/* ----------
* Full months
* ----------
*/
static char *months_full[] = {
"January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December", NULL
};
/* ----------
* AC / DC
* ----------
*/
#define YEAR_ABS(_y) (_y < 0 ? -(_y -1) : _y)
#define BC_STR_ORIG " BC"
#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"
/* ----------
* AM / PM
* ----------
*/
#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"
/* ----------
* Months in roman-numeral
* (Must be conversely for seq_search (in FROM_CHAR), because
* 'VIII' must be over 'V')
* ----------
*/
static char *rm_months_upper[] =
{"XII", "XI", "X", "IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I", NULL};
static char *rm_months_lower[] =
{"xii", "xi", "x", "ix", "viii", "vii", "vi", "v", "iv", "iii", "ii", "i", NULL};
/* ----------
* Roman numbers
* ----------
*/
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};
/* ----------
* Ordinal postfixes
* ----------
*/
static char *numTH[] = {"ST", "ND", "RD", "TH", NULL};
static char *numth[] = {"st", "nd", "rd", "th", NULL};
/* ----------
* Flags & Options:
* ----------
*/
#define TO_CHAR 1
#define FROM_CHAR 2
#define ONE_UPPER 1 /* Name */
#define ALL_UPPER 2 /* NAME */
#define ALL_LOWER 3 /* name */
#define FULL_SIZ 0
#define MAX_MON_LEN 3
#define MAX_DY_LEN 3
#define TH_UPPER 1
#define TH_LOWER 2
/* ----------
* Flags for DCH version
* ----------
*/
static int DCH_global_flag = 0;
#define DCH_F_FX 0x01
#define IS_FX (DCH_global_flag & DCH_F_FX)
/* ----------
* Number description struct
* ----------
*/
typedef struct
{
int pre, /* (count) numbers before decimal */
post, /* (count) numbers after decimal */
lsign, /* want locales sign */
flag, /* number parametrs */
pre_lsign_num, /* tmp value for lsign */
multi, /* multiplier for 'V' */
zero_start, /* position of first zero */
zero_end, /* position of last zero */
need_locale; /* needs it locale */
} NUMDesc;
/* ----------
* Flags for NUMBER version
* ----------
*/
#define NUM_F_DECIMAL 0x01
#define NUM_F_LDECIMAL 0x02
#define NUM_F_ZERO 0x04
#define NUM_F_BLANK 0x08
#define NUM_F_FILLMODE 0x10
#define NUM_F_LSIGN 0x20
#define NUM_F_BRACKET 0x40
#define NUM_F_MINUS 0x80
#define NUM_F_PLUS 0x100
#define NUM_F_ROMAN 0x200
#define NUM_F_MULTI 0x400
#define NUM_LSIGN_PRE -1
#define NUM_LSIGN_POST 1
#define NUM_LSIGN_NONE 0
/* ----------
* Tests
* ----------
*/
#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)
/* ----------
* Format picture cache
* (cache size:
* Number part = NUM_CACHE_SIZE * NUM_CACHE_FIELDS
* Date-time part = DCH_CACHE_SIZE * DCH_CACHE_FIELDS
* )
* ----------
*/
#define NUM_CACHE_SIZE 64
#define NUM_CACHE_FIELDS 16
#define DCH_CACHE_SIZE 128
#define DCH_CACHE_FIELDS 16
typedef struct
{
FormatNode format[DCH_CACHE_SIZE + 1];
char str[DCH_CACHE_SIZE + 1];
int age;
} DCHCacheEntry;
typedef struct
{
FormatNode format[NUM_CACHE_SIZE + 1];
char str[NUM_CACHE_SIZE + 1];
int age;
NUMDesc Num;
} NUMCacheEntry;
/* global cache for --- date/time part */
static DCHCacheEntry DCHCache[DCH_CACHE_FIELDS + 1];
static int n_DCHCache = 0; /* number of entries */
static int DCHCounter = 0;
/* global cache for --- number part */
static NUMCacheEntry NUMCache[NUM_CACHE_FIELDS + 1];
static int n_NUMCache = 0; /* number of entries */
static int NUMCounter = 0;
#define MAX_INT32 (2147483640)
/* ----------
* For char->date/time conversion
* ----------
*/
typedef struct {
int hh, am, pm, mi, ss, ssss, d, dd, ddd, mm, yyyy, bc, iw, ww, w, cc, q, j;
} TmFromChar;
#define ZERO_tmfc( _X ) \
do { \
(_X)->hh= (_X)->am= (_X)->pm= (_X)->mi= (_X)->ss= (_X)->ssss= \
(_X)->d= (_X)->dd= (_X)->ddd= (_X)->mm= (_X)->yyyy= (_X)->bc= \
(_X)->iw= (_X)->ww= (_X)->w= (_X)->cc= (_X)->q= (_X)->j= 0; \
} while(0)
#ifdef DEBUG_TO_FROM_CHAR
#define NOTICE_TMFC \
elog(DEBUG_elog_output, "TMFC:\nhh %d\nam %d\npm %d\nmi %d\nss %d\nssss %d\nd %d\ndd %d\nddd %d\nmm %d\nyyyy
%d\nbc%d\niw %d\nww %d\nw %d\ncc %d\nq %d\nj %d", \
tmfc->hh, tmfc->am, tmfc->pm, tmfc->mi, tmfc->ss, \
tmfc->ssss, tmfc->d, tmfc->dd, tmfc->ddd, tmfc->mm, \
tmfc->yyyy, tmfc->bc, tmfc->iw, tmfc->ww, tmfc->w, \
tmfc->cc, tmfc->q, tmfc->j);
#define NOTICE_TM \
elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon
%d\n",\
tm->tm_sec, tm->tm_year,\
tm->tm_min, tm->tm_wday, tm->tm_hour, tm->tm_yday,\
tm->tm_mday, tm->tm_isdst,tm->tm_mon)
#endif
#define ZERO_tm( _X ) \
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)
/* ----------
* Private global-modul definitions
* ----------
*/
static struct tm _tm, *tm = &_tm;
static TmFromChar _tmfc, *tmfc = &_tmfc;
static char *tzn;
/* ----------
* Utils
* ----------
*/
#ifndef MIN
#define MIN(a,b) (((a)<(b)) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a,b) (((a)>(b)) ? (a) : (b))
#endif
/*****************************************************************************
* KeyWords definition & action
*****************************************************************************/
static int dch_global(int arg, char *inout, int suf, int flag, FormatNode *node);
static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node);
static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node);
/* ----------
* Suffixes:
* ----------
*/
#define DCH_S_FM 0x01
#define DCH_S_TH 0x02
#define DCH_S_th 0x04
#define DCH_S_SP 0x08
/* ----------
* Suffix tests
* ----------
*/
#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)
/* ----------
* Suffixes definition for DATE-TIME TO/FROM CHAR
* ----------
*/
static KeySuffix DCH_suff[] = {
{"FM", 2, DCH_S_FM, SUFFTYPE_PREFIX},
{"fm", 2, DCH_S_FM, SUFFTYPE_PREFIX},
{"TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX},
{"th", 2, DCH_S_th, SUFFTYPE_POSTFIX},
{"SP", 2, DCH_S_SP, SUFFTYPE_POSTFIX},
/* last */
{NULL, 0, 0, 0}
};
/* ----------
* Format-pictures (KeyWord).
*
* The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted
* complicated -to-> easy:
*
* (example: "DDD","DD","Day","D" )
*
* (this specific sort needs the algorithm for sequential search for strings,
* which not has exact end; -> How keyword is in "HH12blabla" ? - "HH"
* or "HH12"? You must first try "HH12", because "HH" is in string, but
* it is not good.
*
* (!)
* Position for the keyword is simular as position in the enum DCH/NUM_poz
* (!)
*
* For fast search is used the 'int index[]', index is ascii table from position
* 32 (' ') to 126 (~), in this index is DCH_ / NUM_ enums for each ASCII
* position or -1 if char is not used in the KeyWord. Search example for
* string "MM":
* 1) see in index to index['M' - 32],
* 2) take keywords position (enum DCH_MM) from index
* 3) run sequential search in keywords[] from this position
*
* ----------
*/
typedef enum
{
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_IW,
DCH_J,
DCH_MI,
DCH_MM,
DCH_MONTH,
DCH_MON,
DCH_Month,
DCH_Mon,
DCH_P_M,
DCH_PM,
DCH_Q,
DCH_RM,
DCH_SSSS,
DCH_SS,
DCH_TZ,
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_iw,
DCH_j,
DCH_mi,
DCH_mm,
DCH_month,
DCH_mon,
DCH_p_m,
DCH_pm,
DCH_q,
DCH_rm,
DCH_ssss,
DCH_ss,
DCH_tz,
DCH_ww,
DCH_w,
DCH_y_yyy,
DCH_yyyy,
DCH_yyy,
DCH_yy,
DCH_y,
/* last */
_DCH_last_
} DCH_poz;
typedef enum
{
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;
/* ----------
* KeyWords for DATE-TIME version
* ----------
*/
static KeyWord DCH_keywords[] = {
/* keyword,len,func.type is in Index */
{"A.D.", 4, dch_date, DCH_A_D}, /* A */
{"A.M.", 4, dch_time, DCH_A_M},
{"AD", 2, dch_date, DCH_AD},
{"AM", 2, dch_time, DCH_AM},
{"B.C.", 4, dch_date, DCH_B_C}, /* B */
{"BC", 2, dch_date, DCH_BC},
{"CC", 2, dch_date, DCH_CC}, /* C */
{"DAY", 3, dch_date, DCH_DAY}, /* D */
{"DDD", 3, dch_date, DCH_DDD},
{"DD", 2, dch_date, DCH_DD},
{"DY", 2, dch_date, DCH_DY},
{"Day", 3, dch_date, DCH_Day},
{"Dy", 2, dch_date, DCH_Dy},
{"D", 1, dch_date, DCH_D},
{"FX", 2, dch_global, DCH_FX}, /* F */
{"HH24", 4, dch_time, DCH_HH24}, /* H */
{"HH12", 4, dch_time, DCH_HH12},
{"HH", 2, dch_time, DCH_HH},
{"IW", 2, dch_date, DCH_IW}, /* I */
{"J", 1, dch_date, DCH_J}, /* J */
{"MI", 2, dch_time, DCH_MI},
{"MM", 2, dch_date, DCH_MM},
{"MONTH", 5, dch_date, DCH_MONTH},
{"MON", 3, dch_date, DCH_MON},
{"Month", 5, dch_date, DCH_Month},
{"Mon", 3, dch_date, DCH_Mon},
{"P.M.", 4, dch_time, DCH_P_M}, /* P */
{"PM", 2, dch_time, DCH_PM},
{"Q", 1, dch_date, DCH_Q}, /* Q */
{"RM", 2, dch_date, DCH_RM}, /* R */
{"SSSS", 4, dch_time, DCH_SSSS}, /* S */
{"SS", 2, dch_time, DCH_SS},
{"TZ", 2, dch_time, DCH_TZ}, /* T */
{"WW", 2, dch_date, DCH_WW}, /* W */
{"W", 1, dch_date, DCH_W},
{"Y,YYY", 5, dch_date, DCH_Y_YYY}, /* Y */
{"YYYY", 4, dch_date, DCH_YYYY},
{"YYY", 3, dch_date, DCH_YYY},
{"YY", 2, dch_date, DCH_YY},
{"Y", 1, dch_date, DCH_Y},
{"a.d.", 4, dch_date, DCH_a_d}, /* a */
{"a.m.", 4, dch_time, DCH_a_m},
{"ad", 2, dch_date, DCH_ad},
{"am", 2, dch_time, DCH_am},
{"b.c.", 4, dch_date, DCH_b_c}, /* b */
{"bc", 2, dch_date, DCH_bc},
{"cc", 2, dch_date, DCH_CC}, /* c */
{"day", 3, dch_date, DCH_day}, /* d */
{"ddd", 3, dch_date, DCH_DDD},
{"dd", 2, dch_date, DCH_DD},
{"dy", 2, dch_date, DCH_dy},
{"d", 1, dch_date, DCH_D},
{"fx", 2, dch_global, DCH_FX}, /* f */
{"hh24", 4, dch_time, DCH_HH24}, /* h */
{"hh12", 4, dch_time, DCH_HH12},
{"hh", 2, dch_time, DCH_HH},
{"iw", 2, dch_date, DCH_IW}, /* i */
{"j", 1, dch_time, DCH_J}, /* j */
{"mi", 2, dch_time, DCH_MI}, /* m */
{"mm", 2, dch_date, DCH_MM},
{"month", 5, dch_date, DCH_month},
{"mon", 3, dch_date, DCH_mon},
{"p.m.", 4, dch_time, DCH_p_m}, /* p */
{"pm", 2, dch_time, DCH_pm},
{"q", 1, dch_date, DCH_Q}, /* q */
{"rm", 2, dch_date, DCH_rm}, /* r */
{"ssss", 4, dch_time, DCH_SSSS}, /* s */
{"ss", 2, dch_time, DCH_SS},
{"tz", 2, dch_time, DCH_tz}, /* t */
{"ww", 2, dch_date, DCH_WW}, /* w */
{"w", 1, dch_date, DCH_W},
{"y,yyy", 5, dch_date, DCH_Y_YYY}, /* y */
{"yyyy", 4, dch_date, DCH_YYYY},
{"yyy", 3, dch_date, DCH_YYY},
{"yy", 2, dch_date, DCH_YY},
{"y", 1, dch_date, DCH_Y},
/* last */
{NULL, 0, NULL, 0}};
/* ----------
* KeyWords for NUMBER version
* ----------
*/
static KeyWord NUM_keywords[] = {
/* keyword, len, func. type is in Index */
{",", 1, NULL, NUM_COMMA}, /* , */
{".", 1, NULL, NUM_DEC}, /* . */
{"0", 1, NULL, NUM_0}, /* 0 */
{"9", 1, NULL, NUM_9}, /* 9 */
{"B", 1, NULL, NUM_B}, /* B */
{"C", 1, NULL, NUM_C}, /* C */
{"D", 1, NULL, NUM_D}, /* D */
{"E", 1, NULL, NUM_E}, /* E */
{"FM", 2, NULL, NUM_FM}, /* F */
{"G", 1, NULL, NUM_G}, /* G */
{"L", 1, NULL, NUM_L}, /* L */
{"MI", 2, NULL, NUM_MI}, /* M */
{"PL", 2, NULL, NUM_PL}, /* P */
{"PR", 2, NULL, NUM_PR},
{"RN", 2, NULL, NUM_RN}, /* R */
{"SG", 2, NULL, NUM_SG}, /* S */
{"SP", 2, NULL, NUM_SP},
{"S", 1, NULL, NUM_S},
{"TH", 2, NULL, NUM_TH}, /* T */
{"V", 1, NULL, NUM_V}, /* V */
{"b", 1, NULL, NUM_B}, /* b */
{"c", 1, NULL, NUM_C}, /* c */
{"d", 1, NULL, NUM_D}, /* d */
{"e", 1, NULL, NUM_E}, /* e */
{"fm", 2, NULL, NUM_FM}, /* f */
{"g", 1, NULL, NUM_G}, /* g */
{"l", 1, NULL, NUM_L}, /* l */
{"mi", 2, NULL, NUM_MI}, /* m */
{"pl", 2, NULL, NUM_PL}, /* p */
{"pr", 2, NULL, NUM_PR},
{"rn", 2, NULL, NUM_rn}, /* r */
{"sg", 2, NULL, NUM_SG}, /* s */
{"sp", 2, NULL, NUM_SP},
{"s", 1, NULL, NUM_S},
{"th", 2, NULL, NUM_th}, /* t */
{"v", 1, NULL, NUM_V}, /* v */
/* last */
{NULL, 0, NULL, 0}};
/* ----------
* KeyWords index for DATE-TIME version
* ----------
*/
static int DCH_index[KeyWord_INDEX_SIZE] = {
/*
0 1 2 3 4 5 6 7 8 9
*/
/*---- first 0..31 chars are skiped ----*/
-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_IW, DCH_J, -1, -1, DCH_MI, -1, -1,
DCH_P_M, DCH_Q, DCH_RM, DCH_SSSS, DCH_TZ, -1, -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_iw, DCH_j, -1, -1, DCH_mi,
-1, -1, DCH_p_m, DCH_q, DCH_rm, DCH_ssss, DCH_tz, -1, -1, DCH_ww,
-1, DCH_y_yyy, -1, -1, -1, -1
/*---- chars over 126 are skiped ----*/
};
/* ----------
* KeyWords index for NUMBER version
* ----------
*/
static int NUM_index[KeyWord_INDEX_SIZE] = {
/*
0 1 2 3 4 5 6 7 8 9
*/
/*---- first 0..31 chars are skiped ----*/
-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
/*---- chars over 126 are skiped ----*/
};
/* ----------
* Number processor struct
* ----------
*/
typedef struct NUMProc
{
int type; /* FROM_CHAR (TO_NUMBER) or TO_CHAR */
NUMDesc *Num; /* number description */
int sign, /* '-' or '+' */
sign_wrote, /* was sign write */
sign_pos, /* pre number sign position */
num_count, /* number of write digits */
num_in, /* is inside number */
num_curr, /* current position in number */
num_pre, /* space before first number */
read_dec, /* to_number - was read dec. point */
read_post; /* to_number - number of dec. digit */
char *number, /* string with number */
*number_p, /* pointer to current number pozition */
*inout, /* in / out buffer */
*inout_p, /* pointer to current inout pozition */
*last_relevant, /* last relevant number after decimal point */
*L_negative_sign,/* Locale */
*L_positive_sign,
*decimal,
*L_thousands_sep,
*L_currency_symbol;
} NUMProc;
/* ----------
* Functions
* ----------
*/
static KeyWord *index_seq_search(char *str, KeyWord *kw, int *index);
static KeySuffix *suff_search(char *str, KeySuffix *suf, int type);
static void NUMDesc_prepare(NUMDesc *num, FormatNode *n);
static void parse_format(FormatNode *node, char *str, KeyWord *kw,
KeySuffix *suf, int *index, int ver, NUMDesc *Num);
static char *DCH_processor(FormatNode *node, char *inout, int flag);
#ifdef DEBUG_TO_FROM_CHAR
static void dump_index(KeyWord *k, int *index);
static void dump_node(FormatNode *node, int max);
#endif
static char *get_th(char *num, int type);
static char *str_numth(char *dest, char *num, int type);
static int int4len(int4 num);
static char *str_toupper(char *buff);
static char *str_tolower(char *buff);
/* static int is_acdc(char *str, int *len); */
static int seq_search(char *name, char **array, int type, int max, int *len);
static int dch_global(int arg, char *inout, int suf, int flag, FormatNode *node);
static int dch_time(int arg, char *inout, int suf, int flag, FormatNode *node);
static int dch_date(int arg, char *inout, int suf, int flag, FormatNode *node);
static char *fill_str(char *str, int c, int max);
static FormatNode *NUM_cache(int len, NUMDesc *Num, char *pars_str, int *flag);
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, int type);
static DCHCacheEntry *DCH_cache_search(char *str);
static DCHCacheEntry *DCH_cache_getnew(char *str);
static NUMCacheEntry *NUM_cache_search(char *str);
static NUMCacheEntry *NUM_cache_getnew(char *str);
/* ----------
* Fast sequential search, use index for data selection which
* go to seq. cycle (it is very fast for non-wanted strings)
* (can't be used binary search in format parsing)
* ----------
*/
static KeyWord *
index_seq_search(char *str, KeyWord *kw, int *index)
{
int poz;
if (!KeyWord_INDEX_FILTER(*str))
return (KeyWord *) NULL;
if ((poz = *(index + (*str - ' '))) > -1)
{
KeyWord *k = kw + poz;
do
{
if (!strncmp(str, k->name, k->len))
return k;
k++;
if (!k->name)
return (KeyWord *) NULL;
} while (*str == *k->name);
}
return (KeyWord *) NULL;
}
static KeySuffix *
suff_search(char *str, KeySuffix *suf, int type)
{
KeySuffix *s;
for (s = suf; s->name != NULL; s++)
{
if (s->type != type)
continue;
if (!strncmp(str, s->name, s->len))
return s;
}
return (KeySuffix *) NULL;
}
/* ----------
* Prepare NUMDesc (number description struct) via FormatNode struct
* ----------
*/
static void
NUMDesc_prepare(NUMDesc *num, FormatNode *n)
{
if (n->type != NODE_TYPE_ACTION)
return;
switch (n->key->id)
{
case NUM_9:
if (IS_BRACKET(num))
elog(ERROR, "to_char/to_number(): '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))
elog(ERROR, "to_char/to_number(): '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;
case NUM_DEC:
if (IS_DECIMAL(num))
elog(ERROR, "to_char/to_number(): not unique decimal poit.");
if (IS_MULTI(num))
elog(ERROR, "to_char/to_number(): can't use 'V' and decimal poin together.");
num->flag |= NUM_F_DECIMAL;
break;
case NUM_FM:
num->flag |= NUM_F_FILLMODE;
break;
case NUM_S:
if (IS_LSIGN(num))
elog(ERROR, "to_char/to_number(): not unique 'S'.");
if (IS_PLUS(num) || IS_MINUS(num) || IS_BRACKET(num))
elog(ERROR, "to_char/to_number(): can't 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))
elog(ERROR, "to_char/to_number(): can't use 'S' and 'MI' together.");
num->flag |= NUM_F_MINUS;
break;
case NUM_PL:
if (IS_LSIGN(num))
elog(ERROR, "to_char/to_number(): can't use 'S' and 'PL' together.");
num->flag |= NUM_F_PLUS;
break;
case NUM_SG:
if (IS_LSIGN(num))
elog(ERROR, "to_char/to_number(): can't 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))
elog(ERROR, "to_char/to_number(): can't 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))
elog(ERROR, "to_char/to_number(): can't use 'V' and decimal poin together.");
num->flag |= NUM_F_MULTI;
break;
case NUM_E:
elog(ERROR, "to_char/to_number(): 'E' is not supported.");
}
return;
}
/* ----------
* Format parser, search small keywords and keyword's suffixes, and make
* format-node tree.
*
* for DATE-TIME & NUMBER version
* ----------
*/
static void
parse_format(FormatNode *node, char *str, KeyWord *kw,
KeySuffix *suf, int *index, int ver, NUMDesc *Num)
{
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 = (KeyWord *) 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 = (KeyWord *) 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;
}
/* ----------
* Call keyword's function for each of (action) node in format-node tree
* ----------
*/
static char *
DCH_processor(FormatNode *node, char *inout, int flag)
{
FormatNode *n;
char *s;
/* ----------
* Zeroing global flags
* ----------
*/
DCH_global_flag = 0;
for (n = node, s = inout; n->type != NODE_TYPE_END; n++)
{
if (n->type == NODE_TYPE_ACTION)
{
int len;
/* ----------
* Call node action function
* ----------
*/
len = n->key->action(n->key->id, s, n->suffix, flag, n);
if (len > 0)
s += len;
else if (len == -1)
continue;
}
else
{
/* ----------
* Remove to output char from input in TO_CHAR
* ----------
*/
if (flag == TO_CHAR)
*s = n->character;
else
{
/* ----------
* Skip blank space in FROM_CHAR's input
* ----------
*/
if (isspace(n->character) && IS_FX == 0)
{
while (*s != '\0' && isspace((int) *(s + 1)))
++s;
}
}
}
++s; /* ! */
}
if (flag == TO_CHAR)
*s = '\0';
return inout;
}
/* ----------
* DEBUG: Dump the FormatNode Tree (debug)
* ----------
*/
#ifdef DEBUG_TO_FROM_CHAR
#define DUMP_THth(_suf) (S_TH(_suf) ? "TH" : (S_th(_suf) ? "th" : " "))
#define DUMP_FM(_suf) (S_FM(_suf) ? "FM" : " ")
static void
dump_node(FormatNode *node, int max)
{
FormatNode *n;
int a;
elog(DEBUG_elog_output, "to_from-char(): DUMP FORMAT");
for (a = 0, n = node; a <= max; n++, a++)
{
if (n->type == NODE_TYPE_ACTION)
elog(DEBUG_elog_output, "%d:\t NODE_TYPE_ACTION '%s'\t(%s,%s)",
a, n->key->name, DUMP_THth(n->suffix), DUMP_FM(n->suffix));
else if (n->type == NODE_TYPE_CHAR)
elog(DEBUG_elog_output, "%d:\t NODE_TYPE_CHAR '%c'", a, n->character);
else if (n->type == NODE_TYPE_END)
{
elog(DEBUG_elog_output, "%d:\t NODE_TYPE_END", a);
return;
}
else
elog(DEBUG_elog_output, "%d:\t UnKnown NODE !!!", a);
}
}
#endif
/*****************************************************************************
* Private utils
*****************************************************************************/
/* ----------
* Return ST/ND/RD/TH for simple (1..9) numbers
* type --> 0 upper, 1 lower
* ----------
*/
static char *
get_th(char *num, int type)
{
int len = strlen(num),
last, seclast;
last = *(num + (len - 1));
if (!isdigit((unsigned char) last))
elog(ERROR, "get_th: '%s' is not 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];
}
return NULL;
}
/* ----------
* Convert string-number to ordinal string-number
* type --> 0 upper, 1 lower
* ----------
*/
static char *
str_numth(char *dest, char *num, int type)
{
sprintf(dest, "%s%s", num, get_th(num, type));
return dest;
}
/* ----------
* Return length of integer writed in string
* ----------
*/
static int
int4len(int4 num)
{
char b[16];
return sprintf(b, "%d", num);
}
/* ----------
* Convert string to upper-string
* ----------
*/
static char *
str_toupper(char *buff)
{
char *p_buff = buff;
while (*p_buff)
{
*p_buff = toupper((unsigned char) *p_buff);
++p_buff;
}
return buff;
}
/* ----------
* Convert string to lower-string
* ----------
*/
static char *
str_tolower(char *buff)
{
char *p_buff = buff;
while (*p_buff)
{
*p_buff = tolower((unsigned char) *p_buff);
++p_buff;
}
return buff;
}
/* ----------
* Sequential search with to upper/lower conversion
* ----------
*/
static int
seq_search(char *name, char **array, int type, int max, int *len)
{
char *p,
*n,
**a;
int last,
i;
*len = 0;
if (!*name)
return -1;
/* set first char */
if (type == ONE_UPPER || ALL_UPPER)
*name = toupper((unsigned char) *name);
else if (type == ALL_LOWER)
*name = 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 = tolower((unsigned char) *n);
else if (type == ALL_UPPER)
*n = 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;
}
#ifdef DEBUG_TO_FROM_CHAR
/* -----------
* DEBUG: Call for debug and for index checking; (Show ASCII char
* and defined keyword for each used position
* ----------
*/
static void
dump_index(KeyWord *k, int *index)
{
int i,
count = 0,
free_i = 0;
elog(DEBUG_elog_output, "TO-FROM_CHAR: Dump KeyWord Index:");
for (i = 0; i < KeyWord_INDEX_SIZE; i++)
{
if (index[i] != -1)
{
elog(DEBUG_elog_output, "\t%c: %s, ", i + 32, k[index[i]].name);
count++;
}
else
{
free_i++;
elog(DEBUG_elog_output, "\t(%d) %c %d", i, i + 32, index[i]);
}
}
elog(DEBUG_elog_output, "\n\t\tUsed positions: %d,\n\t\tFree positions: %d",
count, free_i);
}
#endif
/* ----------
* Skip TM / th in FROM_CHAR
* ----------
*/
#define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0)
/* ----------
* Global format option for DCH version
* ----------
*/
static int
dch_global(int arg, char *inout, int suf, int flag, FormatNode *node)
{
switch (arg)
{
case DCH_FX:
DCH_global_flag |= DCH_F_FX;
break;
}
return -1;
}
#define AMPM_ERROR elog(ERROR, "to_timestamp(): bad AM/PM string")
/* ----------
* Master function of TIME for:
* TO_CHAR - write (inout) formated string
* FROM_CHAR - scan (inout) string by course of FormatNode
* ----------
*/
static int
dch_time(int arg, char *inout, int suf, int flag, FormatNode *node)
{
char *p_inout = inout;
switch (arg)
{
case DCH_A_M:
case DCH_P_M:
if (flag == TO_CHAR)
{
strcpy(inout, ((tm->tm_hour > 11
&& tm->tm_hour < 24) ? P_M_STR : A_M_STR));
return 3;
}
else if (flag == FROM_CHAR)
{
if (strncmp(inout, P_M_STR, 4) == 0)
tmfc->pm = TRUE;
else if (strncmp(inout, A_M_STR, 4) == 0)
tmfc->am = TRUE;
else
AMPM_ERROR;
return 3;
}
break;
case DCH_AM:
case DCH_PM:
if (flag == TO_CHAR)
{
strcpy(inout, ((tm->tm_hour > 11
&& tm->tm_hour < 24) ? PM_STR : AM_STR));
return 1;
}
else if (flag == FROM_CHAR)
{
if (strncmp(inout, PM_STR, 2) == 0)
tmfc->pm = TRUE;
else if (strncmp(inout, AM_STR, 2) == 0)
tmfc->am = TRUE;
else
AMPM_ERROR;
return 1;
}
break;
case DCH_a_m:
case DCH_p_m:
if (flag == TO_CHAR)
{
strcpy(inout, ((tm->tm_hour > 11
&& tm->tm_hour < 24) ? p_m_STR : a_m_STR));
return 3;
}
else if (flag == FROM_CHAR)
{
if (strncmp(inout, p_m_STR, 4) == 0)
tmfc->pm = TRUE;
else if (strncmp(inout, a_m_STR, 4) == 0)
tmfc->am = TRUE;
else
AMPM_ERROR;
return 3;
}
break;
case DCH_am:
case DCH_pm:
if (flag == TO_CHAR)
{
strcpy(inout, ((tm->tm_hour > 11
&& tm->tm_hour < 24) ? pm_STR : am_STR));
return 1;
}
else if (flag == FROM_CHAR)
{
if (strncmp(inout, pm_STR, 2) == 0)
tmfc->pm = TRUE;
else if (strncmp(inout, am_STR, 2) == 0)
tmfc->am = TRUE;
else
AMPM_ERROR;
return 1;
}
break;
case DCH_HH:
case DCH_HH12:
if (flag == TO_CHAR)
{
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
tm->tm_hour == 0 ? 12 :
tm->tm_hour < 13 ? tm->tm_hour : tm->tm_hour - 12);
if (S_THth(suf))
str_numth(p_inout, inout, 0);
if (S_FM(suf) || S_THth(suf))
return strlen(p_inout) - 1;
else
return 1;
}
else if (flag == FROM_CHAR)
{
if (S_FM(suf))
{
sscanf(inout, "%d", &tmfc->hh);
return int4len((int4) tmfc->hh) - 1 + SKIP_THth(suf);
}
else
{
sscanf(inout, "%02d", &tmfc->hh);
return 1 + SKIP_THth(suf);
}
}
break;
case DCH_HH24:
if (flag == TO_CHAR)
{
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_hour);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
if (S_FM(suf) || S_THth(suf))
return strlen(p_inout) - 1;
else
return 1;
}
else if (flag == FROM_CHAR)
{
if (S_FM(suf))
{
sscanf(inout, "%d", &tmfc->hh);
return int4len((int4) tmfc->hh) - 1 + SKIP_THth(suf);
}
else
{
sscanf(inout, "%02d", &tmfc->hh);
return 1 + SKIP_THth(suf);
}
}
break;
case DCH_MI:
if (flag == TO_CHAR)
{
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_min);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
if (S_FM(suf) || S_THth(suf))
return strlen(p_inout) - 1;
else
return 1;
}
else if (flag == FROM_CHAR)
{
if (S_FM(suf))
{
sscanf(inout, "%d", &tmfc->mi);
return int4len((int4) tmfc->mi) - 1 + SKIP_THth(suf);
}
else
{
sscanf(inout, "%02d", &tmfc->mi);
return 1 + SKIP_THth(suf);
}
}
break;
case DCH_SS:
if (flag == TO_CHAR)
{
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_sec);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
if (S_FM(suf) || S_THth(suf))
return strlen(p_inout) - 1;
else
return 1;
}
else if (flag == FROM_CHAR)
{
if (S_FM(suf))
{
sscanf(inout, "%d", &tmfc->ss);
return int4len((int4) tmfc->ss) - 1 + SKIP_THth(suf);
}
else
{
sscanf(inout, "%02d", &tmfc->ss);
return 1 + SKIP_THth(suf);
}
}
break;
case DCH_SSSS:
if (flag == TO_CHAR)
{
sprintf(inout, "%d", tm->tm_hour * 3600 +
tm->tm_min * 60 +
tm->tm_sec);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout) - 1;
}
else if (flag == FROM_CHAR)
{
sscanf(inout, "%d", &tmfc->ssss);
return int4len((int4) tmfc->ssss) - 1 + SKIP_THth(suf);
}
break;
case DCH_tz:
case DCH_TZ:
if (flag == TO_CHAR && tzn)
{
int siz = strlen(tzn);
if (arg == DCH_TZ)
strcpy(inout, tzn);
else
{
char *p = palloc(siz);
strcpy(p, tzn);
strcpy(inout, str_tolower(p));
pfree(p);
}
return siz - 1;
}
else if (flag == FROM_CHAR)
{
elog(ERROR, "to_timestamp(): TZ/tz not supported.");
}
}
return -1;
}
#define CHECK_SEQ_SEARCH(_l, _s) \
do { \
if (_l <= 0) { \
elog(ERROR, "to_timestamp(): bad value for %s", _s); \
} \
} while (0)
/* ----------
* Master of DATE for:
* TO_CHAR - write (inout) formated string
* FROM_CHAR - scan (inout) string by course of FormatNode
* ----------
*/
static int
dch_date(int arg, char *inout, int suf, int flag, FormatNode *node)
{
char buff[DCH_CACHE_SIZE],
*p_inout;
int i,
len;
p_inout = inout;
/* ----------
* In the FROM-char is not difference between "January" or "JANUARY"
* or "january", all is before search convert to "first-upper".
* This convention is used for MONTH, MON, DAY, DY
* ----------
*/
if (flag == FROM_CHAR)
{
if (arg == DCH_MONTH || arg == DCH_Month || arg == DCH_month)
{
tmfc->mm = seq_search(inout, months_full, ONE_UPPER, FULL_SIZ, &len) +1;
CHECK_SEQ_SEARCH(len, "MONTH/Month/month");
if (S_FM(suf))
return len - 1;
else
return 8;
}
else if (arg == DCH_MON || arg == DCH_Mon || arg == DCH_mon)
{
tmfc->mm = seq_search(inout, months, ONE_UPPER, MAX_MON_LEN, &len) +1;
CHECK_SEQ_SEARCH(len, "MON/Mon/mon");
return 2;
}
else if (arg == DCH_DAY || arg == DCH_Day || arg == DCH_day)
{
tmfc->d = seq_search(inout, days, ONE_UPPER, FULL_SIZ, &len);
CHECK_SEQ_SEARCH(len, "DAY/Day/day");
if (S_FM(suf))
return len - 1;
else
return 8;
}
else if (arg == DCH_DY || arg == DCH_Dy || arg == DCH_dy)
{
tmfc->d = seq_search(inout, days, ONE_UPPER, MAX_DY_LEN, &len);
CHECK_SEQ_SEARCH(len, "DY/Dy/dy");
return 2;
}
}
switch (arg)
{
case DCH_A_D:
case DCH_B_C:
if (flag == TO_CHAR)
{
strcpy(inout, (tm->tm_year < 0 ? B_C_STR : A_D_STR));
return 3;
}
else if (flag == FROM_CHAR)
{
if (strncmp(inout, B_C_STR, 4) == 0)
tmfc->bc = TRUE;
return 3;
}
break;
case DCH_AD:
case DCH_BC:
if (flag == TO_CHAR)
{
strcpy(inout, (tm->tm_year < 0 ? BC_STR : AD_STR));
return 1;
}
else if (flag == FROM_CHAR)
{
if (strncmp(inout, BC_STR, 2) == 0)
tmfc->bc = TRUE;
return 1;
}
break;
case DCH_a_d:
case DCH_b_c:
if (flag == TO_CHAR)
{
strcpy(inout, (tm->tm_year < 0 ? b_c_STR : a_d_STR));
return 3;
}
else if (flag == FROM_CHAR)
{
if (strncmp(inout, b_c_STR, 4) == 0)
tmfc->bc = TRUE;
return 3;
}
break;
case DCH_ad:
case DCH_bc:
if (flag == TO_CHAR)
{
strcpy(inout, (tm->tm_year < 0 ? bc_STR : ad_STR));
return 1;
}
else if (flag == FROM_CHAR)
{
if (strncmp(inout, bc_STR, 2) == 0)
tmfc->bc = TRUE;
return 1;
}
break;
case DCH_MONTH:
strcpy(inout, months_full[tm->tm_mon - 1]);
sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(inout));
if (S_FM(suf))
return strlen(p_inout) - 1;
else
return 8;
case DCH_Month:
sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
if (S_FM(suf))
return strlen(p_inout) - 1;
else
return 8;
case DCH_month:
sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
*inout = tolower(*inout);
if (S_FM(suf))
return strlen(p_inout) - 1;
else
return 8;
case DCH_MON:
strcpy(inout, months[tm->tm_mon - 1]);
inout = str_toupper(inout);
return 2;
case DCH_Mon:
strcpy(inout, months[tm->tm_mon - 1]);
return 2;
case DCH_mon:
strcpy(inout, months[tm->tm_mon - 1]);
*inout = tolower(*inout);
return 2;
case DCH_MM:
if (flag == TO_CHAR)
{
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mon);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
if (S_FM(suf) || S_THth(suf))
return strlen(p_inout) - 1;
else
return 1;
}
else if (flag == FROM_CHAR)
{
if (S_FM(suf))
{
sscanf(inout, "%d", &tmfc->mm);
return int4len((int4) tmfc->mm) - 1 + SKIP_THth(suf);
}
else
{
sscanf(inout, "%02d", &tmfc->mm);
return 1 + SKIP_THth(suf);
}
}
break;
case DCH_DAY:
strcpy(inout, days[tm->tm_wday]);
sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(inout));
if (S_FM(suf))
return strlen(p_inout) - 1;
else
return 8;
case DCH_Day:
sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]);
if (S_FM(suf))
return strlen(p_inout) - 1;
else
return 8;
case DCH_day:
sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]);
*inout = tolower(*inout);
if (S_FM(suf))
return strlen(p_inout) - 1;
else
return 8;
case DCH_DY:
strcpy(inout, days[tm->tm_wday]);
inout = str_toupper(inout);
return 2;
case DCH_Dy:
strcpy(inout, days[tm->tm_wday]);
return 2;
case DCH_dy:
strcpy(inout, days[tm->tm_wday]);
*inout = tolower(*inout);
return 2;
case DCH_DDD:
if (flag == TO_CHAR)
{
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 3, tm->tm_yday);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
if (S_FM(suf) || S_THth(suf))
return strlen(p_inout) - 1;
else
return 2;
}
else if (flag == FROM_CHAR)
{
if (S_FM(suf))
{
sscanf(inout, "%d", &tmfc->ddd);
return int4len((int4) tmfc->ddd) - 1 + SKIP_THth(suf);
}
else
{
sscanf(inout, "%03d", &tmfc->ddd);
return 2 + SKIP_THth(suf);
}
}
break;
case DCH_DD:
if (flag == TO_CHAR)
{
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, tm->tm_mday);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
if (S_FM(suf) || S_THth(suf))
return strlen(p_inout) - 1;
else
return 1;
}
else if (flag == FROM_CHAR)
{
if (S_FM(suf))
{
sscanf(inout, "%d", &tmfc->dd);
return int4len((int4) tmfc->dd) - 1 + SKIP_THth(suf);
}
else
{
sscanf(inout, "%02d", &tmfc->dd);
return 1 + SKIP_THth(suf);
}
}
break;
case DCH_D:
if (flag == TO_CHAR)
{
sprintf(inout, "%d", tm->tm_wday + 1);
if (S_THth(suf))
{
str_numth(p_inout, inout, S_TH_TYPE(suf));
return 2;
}
return 0;
}
else if (flag == FROM_CHAR)
{
sscanf(inout, "%1d", &tmfc->d);
return 0 + SKIP_THth(suf);
}
break;
case DCH_WW:
if (flag == TO_CHAR)
{
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
(tm->tm_yday-1) / 7 + 1);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
if (S_FM(suf) || S_THth(suf))
return strlen(p_inout) - 1;
else
return 1;
}
else if (flag == FROM_CHAR)
{
if (S_FM(suf))
{
sscanf(inout, "%d", &tmfc->ww);
return int4len((int4) tmfc->ww) - 1 + SKIP_THth(suf);
}
else
{
sscanf(inout, "%02d", &tmfc->ww);
return 1 + SKIP_THth(suf);
}
}
break;
case DCH_IW:
if (flag == TO_CHAR)
{
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2,
date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday));
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
if (S_FM(suf) || S_THth(suf))
return strlen(p_inout) - 1;
else
return 1;
}
else if (flag == FROM_CHAR)
{
if (S_FM(suf))
{
sscanf(inout, "%d", &tmfc->iw);
return int4len((int4) tmfc->iw) - 1 + SKIP_THth(suf);
}
else
{
sscanf(inout, "%02d", &tmfc->iw);
return 1 + SKIP_THth(suf);
}
}
break;
case DCH_Q:
if (flag == TO_CHAR)
{
sprintf(inout, "%d", (tm->tm_mon - 1) / 3 + 1);
if (S_THth(suf))
{
str_numth(p_inout, inout, S_TH_TYPE(suf));
return 2;
}
return 0;
}
else if (flag == FROM_CHAR)
{
sscanf(inout, "%1d", &tmfc->q);
return 0 + SKIP_THth(suf);
}
break;
case DCH_CC:
if (flag == TO_CHAR)
{
i = tm->tm_year / 100 + 1;
if (i <= 99 && i >= -99)
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 2, i);
else
sprintf(inout, "%d", i);
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout) - 1;
}
else if (flag == FROM_CHAR)
{
sscanf(inout, "%d", &tmfc->cc);
return int4len((int4) tmfc->cc) + SKIP_THth(suf) -1;
}
break;
case DCH_Y_YYY:
if (flag == TO_CHAR)
{
i = YEAR_ABS(tm->tm_year) / 1000;
sprintf(inout, "%d,%03d", i, YEAR_ABS(tm->tm_year) - (i * 1000));
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout) - 1;
}
else if (flag == FROM_CHAR)
{
int cc;
sscanf(inout, "%d,%03d", &cc, &tmfc->yyyy);
tmfc->yyyy += (cc * 1000);
if (!S_FM(suf) && tmfc->yyyy <= 9999 && tmfc->yyyy >= -9999)
len = 5;
else
len = int4len((int4) tmfc->yyyy) + 1;
len += SKIP_THth(suf);
return len - 1;
}
break;
case DCH_YYYY:
if (flag == TO_CHAR)
{
if (tm->tm_year <= 9999 && tm->tm_year >= -9998)
sprintf(inout, "%0*d", S_FM(suf) ? 0 : 4, YEAR_ABS(tm->tm_year));
else
sprintf(inout, "%d", YEAR_ABS(tm->tm_year));
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout) - 1;
}
else if (flag == FROM_CHAR)
{
sscanf(inout, "%d", &tmfc->yyyy);
if (!S_FM(suf) && tmfc->yyyy <= 9999 && tmfc->yyyy >= -9999)
len = 4;
else
len = int4len((int4) tmfc->yyyy);
len += SKIP_THth(suf);
return len - 1;
}
break;
case DCH_YYY:
if (flag == TO_CHAR)
{
sprintf(buff, "%03d", YEAR_ABS(tm->tm_year));
i = strlen(buff);
strcpy(inout, buff + (i - 3));
if (S_THth(suf))
{
str_numth(p_inout, inout, S_TH_TYPE(suf));
return 4;
}
return 2;
}
else if (flag == FROM_CHAR)
{
sscanf(inout, "%03d", &tmfc->yyyy);
return 2 + SKIP_THth(suf);
}
break;
case DCH_YY:
if (flag == TO_CHAR)
{
sprintf(buff, "%02d", YEAR_ABS(tm->tm_year));
i = strlen(buff);
strcpy(inout, buff + (i - 2));
if (S_THth(suf))
{
str_numth(p_inout, inout, S_TH_TYPE(suf));
return 3;
}
return 1;
}
else if (flag == FROM_CHAR)
{
sscanf(inout, "%02d", &tmfc->yyyy);
return 1 + SKIP_THth(suf);
}
break;
case DCH_Y:
if (flag == TO_CHAR)
{
sprintf(buff, "%1d", YEAR_ABS(tm->tm_year));
i = strlen(buff);
strcpy(inout, buff + (i - 1));
if (S_THth(suf))
{
str_numth(p_inout, inout, S_TH_TYPE(suf));
return 2;
}
return 0;
}
else if (flag == FROM_CHAR)
{
sscanf(inout, "%1d", &tmfc->yyyy);
return 0 + SKIP_THth(suf);
}
break;
case DCH_RM:
if (flag == TO_CHAR)
{
sprintf(inout, "%*s", S_FM(suf) ? 0 : -4,
rm_months_upper[12 - tm->tm_mon]);
if (S_FM(suf))
return strlen(p_inout) - 1;
else
return 3;
}
else if (flag == FROM_CHAR)
{
tmfc->mm = 12 - seq_search(inout, rm_months_upper, ALL_UPPER, FULL_SIZ, &len);
CHECK_SEQ_SEARCH(len, "RM");
if (S_FM(suf))
return len - 1;
else
return 3;
}
break;
case DCH_rm:
if (flag == TO_CHAR)
{
sprintf(inout, "%*s", S_FM(suf) ? 0 : -4,
rm_months_lower[12 - tm->tm_mon]);
if (S_FM(suf))
return strlen(p_inout) - 1;
else
return 3;
}
else if (flag == FROM_CHAR)
{
tmfc->mm = 12 - seq_search(inout, rm_months_lower, ALL_UPPER, FULL_SIZ, &len);
CHECK_SEQ_SEARCH(len, "rm");
if (S_FM(suf))
return len - 1;
else
return 3;
}
break;
case DCH_W:
if (flag == TO_CHAR)
{
sprintf(inout, "%d", (tm->tm_mday-1) / 7 + 1);
if (S_THth(suf))
{
str_numth(p_inout, inout, S_TH_TYPE(suf));
return 2;
}
return 0;
}
else if (flag == FROM_CHAR)
{
sscanf(inout, "%1d", &tmfc->w);
return 0 + SKIP_THth(suf);
}
break;
case DCH_J:
if (flag == TO_CHAR)
{
sprintf(inout, "%d", date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
if (S_THth(suf))
str_numth(p_inout, inout, S_TH_TYPE(suf));
return strlen(p_inout) - 1;
}
else if (flag == FROM_CHAR)
{
sscanf(inout, "%d", &tmfc->j);
return int4len((int4) tmfc->j) + SKIP_THth(suf) -1;
}
break;
}
return -1;
}
static DCHCacheEntry *
DCH_cache_getnew(char *str)
{
DCHCacheEntry *ent = NULL;
/* counter overload check - paranoa? */
if (DCHCounter + DCH_CACHE_FIELDS >= MAX_INT32)
{
DCHCounter = 0;
for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
ent->age = (++DCHCounter);
}
/* ----------
* Cache is full - needs remove any older 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; 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
strcpy(old->str, str); /* check str size before this func. */
/* 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;
strcpy(ent->str, str); /* check str size before this func. */
/* ent->format fill parser */
ent->age = (++DCHCounter);
++n_DCHCache;
return ent;
}
return (DCHCacheEntry *) NULL; /* never */
}
static DCHCacheEntry *
DCH_cache_search(char *str)
{
int i = 0;
DCHCacheEntry *ent;
/* counter overload check - paranoa? */
if (DCHCounter + DCH_CACHE_FIELDS >= MAX_INT32)
{
DCHCounter = 0;
for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
ent->age = (++DCHCounter);
}
for (ent = DCHCache; ent <= (DCHCache + DCH_CACHE_FIELDS); ent++)
{
if (i == n_DCHCache)
break;
if (strcmp(ent->str, str) == 0)
{
ent->age = (++DCHCounter);
return ent;
}
i++;
}
return (DCHCacheEntry *) NULL;
}
/****************************************************************************
* Public routines
***************************************************************************/
/* -------------------
* TIMESTAMP to_char()
* -------------------
*/
Datum
timestamp_to_char(PG_FUNCTION_ARGS)
{
Timestamp dt = PG_GETARG_TIMESTAMP(0);
text *fmt = PG_GETARG_TEXT_P(1);
text *result,
*result_tmp;
FormatNode *format;
char *str;
double fsec;
int len = 0,
tz,
flag = 0,
x = 0;
len = VARSIZE(fmt) - VARHDRSZ;
if (len <= 0 || TIMESTAMP_NOT_FINITE(dt))
PG_RETURN_NULL();
ZERO_tm(tm);
tzn = NULL;
if (TIMESTAMP_IS_EPOCH(dt))
{
x = timestamp2tm(SetTimestamp(dt), NULL, tm, &fsec, NULL);
}
else if (TIMESTAMP_IS_CURRENT(dt))
{
x = timestamp2tm(SetTimestamp(dt), &tz, tm, &fsec, &tzn);
}
else
x = timestamp2tm(dt, &tz, tm, &fsec, &tzn);
if (x != 0)
elog(ERROR, "to_char(): Unable to convert timestamp to tm");
tm->tm_wday = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1) % 7;
tm->tm_yday = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(tm->tm_year, 1, 1) + 1;
/* ----------
* Convert VARDATA() to string
* ----------
*/
str = (char *) palloc(len + 1);
memcpy(str, VARDATA(fmt), len);
*(str + len) = '\0';
/* ----------
* Allocate result
* ----------
*/
result = (text *) palloc((len * DCH_MAX_ITEM_SIZ) + 1 + VARHDRSZ);
/* ----------
* Allocate new memory if format picture is bigger than static cache
* and not use cache (call parser always) - flag=1 show this variant
* ----------
*/
if (len > DCH_CACHE_SIZE)
{
format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
flag = 1;
parse_format(format, str, DCH_keywords,
DCH_suff, DCH_index, DCH_TYPE, NULL);
(format + len)->type = NODE_TYPE_END; /* Paranoa? */
}
else
{
/* ----------
* Use cache buffers
* ----------
*/
DCHCacheEntry *ent;
flag = 0;
if ((ent = DCH_cache_search(str)) == NULL)
{
ent = DCH_cache_getnew(str);
/* ----------
* Not in the cache, must run parser and save a new
* format-picture to the cache.
* ----------
*/
parse_format(ent->format, str, DCH_keywords,
DCH_suff, DCH_index, DCH_TYPE, NULL);
(ent->format + len)->type = NODE_TYPE_END; /* Paranoa? */
#ifdef DEBUG_TO_FROM_CHAR
/* dump_node(ent->format, len); */
/* dump_index(DCH_keywords, DCH_index); */
#endif
}
format = ent->format;
}
DCH_processor(format, VARDATA(result), TO_CHAR);
if (flag)
pfree(format);
pfree(str);
/* ----------
* for result is allocated max memory, which current format-picture
* needs, now it must be re-allocate to result real size
* ----------
*/
if (!(len = strlen(VARDATA(result))))
{
pfree(result);
PG_RETURN_NULL();
}
result_tmp = result;
result = (text *) palloc(len + 1 + VARHDRSZ);
strcpy(VARDATA(result), VARDATA(result_tmp));
VARATT_SIZEP(result) = len + VARHDRSZ;
pfree(result_tmp);
PG_RETURN_TEXT_P(result);
}
/* ---------------------
* TO_TIMESTAMP()
*
* Make Timestamp from date_str which is formated at argument 'fmt'
* ( to_timestamp is reverse to_char() )
* ---------------------
*/
Datum
to_timestamp(PG_FUNCTION_ARGS)
{
text *date_str = PG_GETARG_TEXT_P(0);
text *fmt = PG_GETARG_TEXT_P(1);
FormatNode *format;
int flag = 0;
Timestamp result;
char *str;
int len = 0,
fsec = 0,
tz = 0;
ZERO_tm(tm);
ZERO_tmfc(tmfc);
len = VARSIZE(fmt) - VARHDRSZ;
if (len)
{
/* ----------
* Convert VARDATA() to string
* ----------
*/
str = (char *) palloc(len + 1);
memcpy(str, VARDATA(fmt), len);
*(str + len) = '\0';
/* ----------
* Allocate new memory if format picture is bigger than static cache
* and not use cache (call parser always) - flag=1 show this variant
* ----------
*/
if (len > DCH_CACHE_SIZE)
{
format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
flag = 1;
parse_format(format, str, DCH_keywords,
DCH_suff, DCH_index, DCH_TYPE, NULL);
(format + len)->type = NODE_TYPE_END; /* Paranoa? */
}
else
{
/* ----------
* Use cache buffers
* ----------
*/
DCHCacheEntry *ent;
flag = 0;
if ((ent = DCH_cache_search(str)) == NULL)
{
ent = DCH_cache_getnew(str);
/* ----------
* Not in the cache, must run parser and save a new
* format-picture to the cache.
* ----------
*/
parse_format(ent->format, str, DCH_keywords,
DCH_suff, DCH_index, DCH_TYPE, NULL);
(ent->format + len)->type = NODE_TYPE_END; /* Paranoa? */
#ifdef DEBUG_TO_FROM_CHAR
/* dump_node(ent->format, len); */
/* dump_index(DCH_keywords, DCH_index); */
#endif
}
format = ent->format;
}
/* ----------
* Call action for each node in FormatNode tree
* ----------
*/
#ifdef DEBUG_TO_FROM_CHAR
/* dump_node(format, len); */
#endif
VARDATA(date_str)[VARSIZE(date_str) - VARHDRSZ] = '\0';
DCH_processor(format, VARDATA(date_str), FROM_CHAR);
pfree(str);
if (flag)
pfree(format);
}
/* --------------------------------------------------------------
* Convert values that user define for FROM_CHAR (to_date/to_timestamp)
* to standard 'tm'
* ----------
*/
#ifdef DEBUG_TO_FROM_CHAR
NOTICE_TMFC;
#endif
if (tmfc->ssss)
{
int x;
if (tmfc->ssss > 3600)
tm->tm_sec = x - ((tm->tm_min = (x = tmfc->ssss -
((tm->tm_hour= tmfc->ssss / 3600) * 3600)) / 60) * 60);
else if (tmfc->ssss > 60)
tm->tm_sec = tmfc->ssss - ((tm->tm_min = tmfc->ssss / 60) * 60);
else
tm->tm_sec = tmfc->ssss;
}
if (tmfc->cc)
tm->tm_year = (tmfc->cc-1) * 100;
if (tmfc->ww)
tmfc->ddd = (tmfc->ww - 1) * 7 + 1;
if (tmfc->w)
tmfc->dd = (tmfc->w - 1) * 7 + 1;
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->pm || tmfc->am)
{
if (tm->tm_hour < 1 || tm->tm_hour > 12)
elog(ERROR, "to_timestamp(): AM/PM hour must be between 1 and 12");
if (tmfc->pm && tm->tm_hour < 12)
tm->tm_hour += 12;
else if (tmfc->am && tm->tm_hour == 12)
tm->tm_hour = 0;
}
switch (tmfc->q)
{
case 1: tm->tm_mday = 1; tm->tm_mon = 1; break;
case 2: tm->tm_mday = 1; tm->tm_mon = 4; break;
case 3: tm->tm_mday = 1; tm->tm_mon = 7; break;
case 4: tm->tm_mday = 1; tm->tm_mon = 10; break;
}
if (tmfc->yyyy)
tm->tm_year = tmfc->yyyy;
if (tmfc->j)
j2date(tmfc->j, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
if (tmfc->bc && tm->tm_year > 0)
tm->tm_year = -(tm->tm_year);
if (tm->tm_year < 0)
tm->tm_year = tm->tm_year + 1;
if (tmfc->iw)
isoweek2date(tmfc->iw, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
if (tmfc->d) tm->tm_wday = tmfc->d;
if (tmfc->dd) tm->tm_mday = tmfc->dd;
if (tmfc->ddd) tm->tm_yday = tmfc->ddd;
if (tmfc->mm) tm->tm_mon = tmfc->mm;
/*
* we not ignore DDD
*/
if (tmfc->ddd && (tm->tm_mon <=1 || tm->tm_mday <=1))
{
/* count mday and mon from yday */
int *y, i;
int ysum[2][13] = {
{ 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, 0 }};
if (!tm->tm_year)
elog(ERROR, "to_timestamp() cat't convert yday without year information");
y = ysum[ isleap(tm->tm_year) ];
for (i=0; i <= 11; i++)
{
if (tm->tm_yday < y[i])
break;
}
if (tm->tm_mon <=1)
tm->tm_mon = i+1;
if (tm->tm_mday <=1)
tm->tm_mday = i == 0 ? tm->tm_yday :
tm->tm_yday - y[i-1];
}
/* -------------------------------------------------------------- */
#ifdef DEBUG_TO_FROM_CHAR
NOTICE_TM;
#endif
if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
{
#ifdef USE_POSIX_TIME
tm->tm_isdst = -1;
tm->tm_year -= 1900;
tm->tm_mon -= 1;
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "TO-FROM_CHAR: Call mktime()");
NOTICE_TM;
#endif
mktime(tm);
tm->tm_year += 1900;
tm->tm_mon += 1;
#if defined(HAVE_TM_ZONE)
tz = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */
#elif defined(HAVE_INT_TIMEZONE)
#ifdef __CYGWIN__
tz = (tm->tm_isdst ? (_timezone - 3600) : _timezone);
#else
tz = (tm->tm_isdst ? (timezone - 3600) : timezone);
#endif
#else
#error USE_POSIX_TIME is defined but neither HAVE_TM_ZONE or HAVE_INT_TIMEZONE are defined
#endif
#else /* !USE_POSIX_TIME */
tz = CTimeZone;
#endif
}
else
{
tm->tm_isdst = 0;
tz = 0;
}
#ifdef DEBUG_TO_FROM_CHAR
NOTICE_TM;
#endif
if (tm2timestamp(tm, fsec, &tz, &result) != 0)
elog(ERROR, "to_timestamp(): can't convert 'tm' to timestamp.");
PG_RETURN_TIMESTAMP(result);
}
/* ----------
* TO_DATE
* Make Date from date_str which is formated at argument 'fmt'
* ----------
*/
Datum
to_date(PG_FUNCTION_ARGS)
{
/* Quick hack: since our inputs are just like to_timestamp,
* hand over the whole input info struct...
*/
return DirectFunctionCall1(timestamp_date, to_timestamp(fcinfo));
}
/**********************************************************************
* the NUMBER version part
*********************************************************************/
static char *
fill_str(char *str, int c, int max)
{
memset(str, c, max);
*(str + max + 1) = '\0';
return str;
}
#define zeroize_NUM(_n) \
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)
static NUMCacheEntry *
NUM_cache_getnew(char *str)
{
NUMCacheEntry *ent = NULL;
/* counter overload check - paranoa? */
if (NUMCounter + NUM_CACHE_FIELDS >= MAX_INT32)
{
NUMCounter = 0;
for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
ent->age = (++NUMCounter);
}
/* ----------
* Cache is full - needs remove any older 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++)
{
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
strcpy(old->str, str); /* check str size before this func. */
/* 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;
strcpy(ent->str, str); /* check str size before this func. */
/* ent->format fill parser */
ent->age = (++NUMCounter);
++n_NUMCache;
}
zeroize_NUM(&ent->Num);
return ent; /* never */
}
static NUMCacheEntry *
NUM_cache_search(char *str)
{
int i = 0;
NUMCacheEntry *ent;
/* counter overload check - paranoa? */
if (NUMCounter + NUM_CACHE_FIELDS >= MAX_INT32)
{
NUMCounter = 0;
for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
ent->age = (++NUMCounter);
}
for (ent = NUMCache; ent <= (NUMCache + NUM_CACHE_FIELDS); ent++)
{
if (i == n_NUMCache)
break;
if (strcmp(ent->str, str) == 0)
{
ent->age = (++NUMCounter);
return ent;
}
i++;
}
return (NUMCacheEntry *) NULL;
}
/* ----------
* Cache routine for NUM to_char version
* ----------
*/
static FormatNode *
NUM_cache(int len, NUMDesc *Num, char *pars_str, int *flag)
{
FormatNode *format = NULL;
char *str;
/* ----------
* Convert VARDATA() to string
* ----------
*/
str = (char *) palloc(len + 1);
memcpy(str, pars_str, len);
*(str + len) = '\0';
/* ----------
* Allocate new memory if format picture is bigger than static cache
* and not use cache (call parser always) - flag=1 show this variant
* ----------
*/
if (len > NUM_CACHE_SIZE)
{
format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
*flag = 1;
zeroize_NUM(Num);
parse_format(format, str, NUM_keywords,
NULL, NUM_index, NUM_TYPE, Num);
(format + len)->type = NODE_TYPE_END; /* Paranoa? */
}
else
{
/* ----------
* Use cache buffers
* ----------
*/
NUMCacheEntry *ent;
flag = 0;
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; /* Paranoa? */
}
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 char *
int_to_roman(int number)
{
int len = 0,
num = 0,
set = 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 = sprintf(numstr, "%d", number);
for (p = numstr; *p != '\0'; p++, --len)
{
num = *p - 49; /* 48 ascii + 1 */
if (num < 0)
continue;
if (num == -1 && set == 0)
continue;
set = 1;
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;
}
/* ----------
* Locale
* ----------
*/
static void
NUM_prepare_locale(NUMProc *Np)
{
#ifdef USE_LOCALE
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 thousands separator
* ----------
*/
if (lconv->thousands_sep && *lconv->thousands_sep)
Np->L_thousands_sep = lconv->thousands_sep;
else
Np->L_thousands_sep = ",";
/* ----------
* Number decimal point
* ----------
*/
if (lconv->decimal_point && *lconv->decimal_point)
Np->decimal = lconv->decimal_point;
else
Np->decimal = ".";
/* ----------
* Currency symbol
* ----------
*/
if (lconv->currency_symbol && *lconv->currency_symbol)
Np->L_currency_symbol = lconv->currency_symbol;
else
Np->L_currency_symbol = " ";
if (!IS_LDECIMAL(Np->Num))
Np->decimal = ".";
}
else
{
#endif
/* ----------
* Default values
* ----------
*/
Np->L_negative_sign = "-";
Np->L_positive_sign = "+";
Np->decimal = ".";
Np->L_thousands_sep = ",";
Np->L_currency_symbol = " ";
#ifdef USE_LOCALE
}
#endif
}
/* ----------
* Return pointer of last relevant number after decimal point
* 12.0500 --> last relevant is '5'
* ----------
*/
static char *
get_last_relevant_decnum(char *num)
{
char *result,
*p = strchr(num, '.');
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "CALL: get_last_relevant_decnum()");
#endif
if (!p)
p = num;
result = p;
while (*(++p))
{
if (*p != '0')
result = p;
}
return result;
}
/* ----------
* Number extraction for TO_NUMBER()
* ----------
*/
static void
NUM_numpart_from_char(NUMProc *Np, int id, int plen)
{
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, " --- scan start --- ");
#endif
if (*Np->inout_p == ' ')
Np->inout_p++;
#define OVERLOAD_TEST (Np->inout_p >= Np->inout + plen)
if (*Np->inout_p == ' ')
Np->inout_p++;
if (OVERLOAD_TEST)
return;
/* ----------
* read sign
* ----------
*/
if (*Np->number == ' ' && (id == NUM_0 || id == NUM_9 || NUM_S))
{
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "Try read sign (%c).", *Np->inout_p);
#endif
/* ----------
* locale sign
* ----------
*/
if (IS_LSIGN(Np->Num))
{
int x = strlen(Np->L_negative_sign);
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "Try read locale sign (%c).", *Np->inout_p);
#endif
if (!strncmp(Np->inout_p, Np->L_negative_sign, x))
{
Np->inout_p += x - 1;
*Np->number = '-';
return;
}
x = strlen(Np->L_positive_sign);
if (!strncmp(Np->inout_p, Np->L_positive_sign, x))
{
Np->inout_p += x - 1;
*Np->number = '+';
return;
}
}
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "Try read sipmle 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;
/* ----------
* 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++;
#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))
{
#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;
}
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 (!strncmp(Np->inout_p, Np->decimal, x))
{
Np->inout_p += x - 1;
*Np->number_p = '.';
Np->number_p++;
Np->read_dec = TRUE;
}
}
}
}
/* ----------
* Add digit or sign to number-string
* ----------
*/
static void
NUM_numpart_to_char(NUMProc *Np, int id)
{
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 (Np->num_curr == Np->sign_pos && Np->sign_wrote == FALSE)
{
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "Writing sign to position: %d", Np->num_curr);
#endif
if (IS_LSIGN(Np->Num))
{
/* ----------
* Write locale SIGN
* ----------
*/
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);
}
else if (IS_BRACKET(Np->Num))
{
*Np->inout_p = '<'; /* Write < */
++Np->inout_p;
}
else if (Np->sign == '+')
{
*Np->inout_p = ' '; /* Write + */
++Np->inout_p;
}
else if (Np->sign == '-')
{ /* Write - */
*Np->inout_p = '-';
++Np->inout_p;
}
Np->sign_wrote = TRUE;
}
else if (Np->sign_wrote && IS_BRACKET(Np->Num) &&
(Np->num_curr == Np->num_count + (Np->num_pre ? 1 : 0)
+ (IS_DECIMAL(Np->Num) ? 1 : 0)))
{
/* ----------
* Write close BRACKET
* ----------
*/
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "Writing bracket to position %d", Np->num_curr);
#endif
*Np->inout_p = '>'; /* Write '>' */
++Np->inout_p;
}
/* ----------
* digits / FM / Zero / Dec. point
* ----------
*/
if (id == NUM_9 || id == NUM_0 || id == NUM_D || id == NUM_DEC ||
(id == NUM_S && Np->num_curr < Np->num_pre))
{
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))
{
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "Writing blank space to position %d", Np->num_curr);
#endif
*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
* ----------
*/
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "Writing zero to position %d", Np->num_curr);
#endif
*Np->inout_p = '0'; /* Write '0' */
++Np->inout_p;
Np->num_in = TRUE;
}
else
{
/* ----------
* Write Decinal point
* ----------
*/
if (*Np->number_p == '.')
{
if (!Np->last_relevant || *Np->last_relevant != '.')
{
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "Writing decimal point to position %d", Np->num_curr);
#endif
strcpy(Np->inout_p, Np->decimal); /* Write DEC/D */
Np->inout_p += strlen(Np->inout_p);
/*
* terrible Ora '0' -- 9.9 --> '0.'
*/
}
else if (IS_FILLMODE(Np->Num) && *Np->number == '0' &&
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)
;
/*
* terrible Ora format: '0.1' -- 9.9 --> ' .1'
*/
else if (!IS_ZERO(Np->Num) && *Np->number == '0' &&
Np->number == Np->number_p && Np->Num->post != 0)
{
if (!IS_FILLMODE(Np->Num))
{
*Np->inout_p = ' ';
++Np->inout_p;
/*
* total terible Ora: '0' -- FM9.9 --> '0.'
*/
}
else if (Np->last_relevant && *Np->last_relevant == '.')
{
*Np->inout_p = '0';
++Np->inout_p;
}
}
else
{
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "Writing digit '%c' to position %d", *Np->number_p, Np->num_curr);
#endif
*Np->inout_p = *Np->number_p; /* Write DIGIT */
++Np->inout_p;
Np->num_in = TRUE;
}
}
++Np->number_p;
}
}
++Np->num_curr;
}
static char *
NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
int plen, int sign, int type)
{
FormatNode *n;
NUMProc _Np,
*Np = &_Np;
Np->Num = Num;
Np->type = type;
Np->number = number;
Np->inout = inout;
Np->last_relevant = NULL;
Np->read_post = 0;
Np->read_dec = FALSE;
if (Np->Num->zero_start)
--Np->Num->zero_start;
/* ----------
* Roman correction
* ----------
*/
if (IS_ROMAN(Np->Num))
{
if (Np->type == FROM_CHAR)
elog(ERROR, "to_number(): RN is not supported");
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 (type == FROM_CHAR)
{
Np->sign = FALSE;
Np->sign_pos = -1;
}
else
{
Np->sign = sign;
if (Np->sign != '-')
{
Np->Num->flag &= ~NUM_F_BRACKET;
Np->Num->flag &= ~NUM_F_MINUS;
}
else if (Np->sign != '+')
Np->Num->flag &= ~NUM_F_PLUS;
if (Np->sign == '+' && IS_FILLMODE(Np->Num) && !IS_LSIGN(Np->Num))
Np->sign_wrote = TRUE; /* needn't sign */
else
Np->sign_wrote = FALSE; /* need sign */
Np->sign_pos = -1;
if (Np->Num->lsign == NUM_LSIGN_PRE && Np->Num->pre == Np->Num->pre_lsign_num)
Np->Num->lsign = NUM_LSIGN_POST;
/* MI/PL/SG - write sign itself and not in number */
if (IS_PLUS(Np->Num) || IS_MINUS(Np->Num))
Np->sign_wrote = TRUE; /* needn't sign */
}
/* ----------
* Count
* ----------
*/
Np->num_count = Np->Num->post + Np->Num->pre - 1;
if (type == TO_CHAR)
{
Np->num_pre = plen;
if (IS_FILLMODE(Np->Num))
{
if (IS_DECIMAL(Np->Num))
Np->last_relevant = get_last_relevant_decnum(
Np->number +
((Np->Num->zero_end - Np->num_pre > 0) ?
Np->Num->zero_end - Np->num_pre : 0));
}
if (!Np->sign_wrote && Np->num_pre == 0)
++Np->num_count;
if (!Np->sign_wrote)
{
/* ----------
* Set SING position
* ----------
*/
if (Np->Num->lsign == NUM_LSIGN_POST)
{
Np->sign_pos = Np->num_count + (Np->num_pre ? 1 : 0);
if (IS_DECIMAL(Np->Num)) /* decimal point correctio */
++Np->sign_pos;
}
else if (IS_ZERO(Np->Num) && Np->num_pre > Np->Num->zero_start)
Np->sign_pos = Np->Num->zero_start ? Np->Num->zero_start : 0;
else
Np->sign_pos = Np->num_pre && !IS_FILLMODE(Np->Num) ? Np->num_pre : 0;
/* ----------
* terrible Ora format
* ----------
*/
if (!IS_ZERO(Np->Num) && *Np->number == '0' &&
!IS_FILLMODE(Np->Num) && Np->Num->post != 0)
{
++Np->sign_pos;
if (IS_LSIGN(Np->Num))
{
if (Np->Num->lsign == NUM_LSIGN_PRE)
++Np->sign_pos;
else
--Np->sign_pos;
}
}
}
}
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\tNUM: '%s'\n\tPRE: %d\n\tPOST: %d\n\tNUM_COUNT: %d\n\tNUM_PRE: %d\n\tSIGN_POS: %d\n\tSIGN_WROTE:
%s\n\tZERO:%s\n\tZERO_START: %d\n\tZERO_END: %d\n\tLAST_RELEVANT: %s",
Np->number,
Np->Num->pre,
Np->Num->post,
Np->num_count,
Np->num_pre,
Np->sign_pos,
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>"
);
#endif
/* ----------
* Locale
* ----------
*/
NUM_prepare_locale(Np);
/* ----------
* Processor direct cycle
* ----------
*/
if (Np->type == FROM_CHAR)
Np->number_p = Np->number + 1; /* first char is space for sign */
else if (Np->type == TO_CHAR)
Np->number_p = Np->number;
for (n = node, Np->inout_p = Np->inout; n->type != NODE_TYPE_END; n++)
{
if (Np->type == FROM_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
* ----------
*/
switch (n->key->id)
{
case NUM_9:
case NUM_0:
case NUM_DEC:
case NUM_D:
case NUM_S:
case NUM_PR:
if (Np->type == 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->type == TO_CHAR)
{
if (!Np->num_in)
{
if (IS_FILLMODE(Np->Num))
continue;
else
*Np->inout_p = ' ';
}
else
*Np->inout_p = ',';
}
else if (Np->type == FROM_CHAR)
{
if (!Np->num_in)
{
if (IS_FILLMODE(Np->Num))
continue;
}
}
break;
case NUM_G:
if (Np->type == 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->type == FROM_CHAR)
{
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->type == TO_CHAR)
{
strcpy(Np->inout_p, Np->L_currency_symbol);
Np->inout_p += strlen(Np->inout_p) - 1;
}
else if (Np->type == FROM_CHAR)
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
Np->inout_p += sprintf(Np->inout_p, "%15s", Np->number_p) - 1;
break;
case NUM_rn:
if (IS_FILLMODE(Np->Num))
{
strcpy(Np->inout_p, str_tolower(Np->number_p));
Np->inout_p += strlen(Np->inout_p) - 1;
}
else
Np->inout_p += sprintf(Np->inout_p, "%15s", str_tolower(Np->number_p)) - 1;
break;
case NUM_th:
if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
Np->sign == '-' || IS_DECIMAL(Np->Num))
continue;
if (Np->type == 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->type == TO_CHAR)
strcpy(Np->inout_p, get_th(Np->number, TH_UPPER));
Np->inout_p += 1;
break;
case NUM_MI:
if (Np->type == TO_CHAR)
{
if (Np->sign == '-')
*Np->inout_p = '-';
else
*Np->inout_p = ' ';
}
else if (Np->type == FROM_CHAR)
{
if (*Np->inout_p == '-')
*Np->number = '-';
}
break;
case NUM_PL:
if (Np->type == TO_CHAR)
{
if (Np->sign == '+')
*Np->inout_p = '+';
else
*Np->inout_p = ' ';
}
else if (Np->type == FROM_CHAR)
{
if (*Np->inout_p == '+')
*Np->number = '+';
}
break;
case NUM_SG:
if (Np->type == TO_CHAR)
*Np->inout_p = Np->sign;
else if (Np->type == FROM_CHAR)
{
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->type == TO_CHAR)
*Np->inout_p = n->character;
}
Np->inout_p++;
}
if (Np->type == TO_CHAR)
{
*Np->inout_p = '\0';
return Np->inout;
}
else if (Np->type == FROM_CHAR)
{
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;
}
else
return NULL;
return NULL;
}
/* ----------
* MACRO: Start part of NUM - for all NUM's to_char variants
* (sorry, but I hate copy same code - macro is better..)
* ----------
*/
#define NUM_TOCHAR_prepare \
do { \
len = VARSIZE(fmt) - VARHDRSZ; \
if (len <= 0) \
return DirectFunctionCall1(textin, CStringGetDatum("")); \
result = (text *) palloc( (len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ); \
format = NUM_cache(len, &Num, VARDATA(fmt), &flag); \
} while (0)
/* ----------
* MACRO: Finish part of NUM
* ----------
*/
#define NUM_TOCHAR_finish \
do { \
NUM_processor(format, &Num, VARDATA(result), \
numstr, plen, sign, TO_CHAR); \
pfree(orgnum); \
\
if (flag) \
pfree(format); \
\
/* ---------- \
* for result is allocated max memory, which current format-picture\
* needs, now it must be re-allocate to result real size \
* ---------- \
*/ \
if (!(len = strlen(VARDATA(result)))) \
{ \
pfree(result); \
PG_RETURN_NULL(); \
} \
\
result_tmp = result; \
result = (text *) palloc( len + 1 + VARHDRSZ); \
\
strcpy( VARDATA(result), VARDATA(result_tmp)); \
VARATT_SIZEP(result) = len + VARHDRSZ; \
pfree(result_tmp); \
} while(0)
/* -------------------
* NUMERIC to_number() (convert string to numeric)
* -------------------
*/
Datum
numeric_to_number(PG_FUNCTION_ARGS)
{
text *value = PG_GETARG_TEXT_P(0);
text *fmt = PG_GETARG_TEXT_P(1);
NUMDesc Num;
Datum result;
FormatNode *format;
char *numstr;
int flag = 0;
int len = 0;
int scale,
precision;
len = VARSIZE(fmt) - VARHDRSZ;
if (len <= 0)
PG_RETURN_NULL();
format = NUM_cache(len, &Num, VARDATA(fmt), &flag);
numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1);
NUM_processor(format, &Num, VARDATA(value), numstr,
VARSIZE(value) - VARHDRSZ, 0, FROM_CHAR);
scale = Num.post;
precision = MAX(0, Num.pre) + scale;
if (flag)
pfree(format);
result = DirectFunctionCall3(numeric_in,
CStringGetDatum(numstr),
ObjectIdGetDatum(InvalidOid),
Int32GetDatum(((precision << 16) | scale) + VARHDRSZ));
pfree(numstr);
return result;
}
/* ------------------
* NUMERIC to_char()
* ------------------
*/
Datum
numeric_to_char(PG_FUNCTION_ARGS)
{
Numeric value = PG_GETARG_NUMERIC(0);
text *fmt = PG_GETARG_TEXT_P(1);
NUMDesc Num;
FormatNode *format;
text *result,
*result_tmp;
int flag = 0;
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))));
pfree(x);
}
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)));
pfree(x);
pfree(a);
pfree(b);
Num.pre += Num.multi;
}
x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
NumericGetDatum(val),
Int32GetDatum(Num.post)));
orgnum = DatumGetCString(DirectFunctionCall1(numeric_out,
NumericGetDatum(x)));
pfree(x);
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)
{
fill_str(numstr, '#', Num.pre);
*(numstr + Num.pre) = '.';
fill_str(numstr + 1 + Num.pre, '#', Num.post);
}
if (IS_MULTI(&Num))
pfree(val);
}
NUM_TOCHAR_finish;
PG_RETURN_TEXT_P(result);
}
/* ---------------
* INT4 to_char()
* ---------------
*/
Datum
int4_to_char(PG_FUNCTION_ARGS)
{
int32 value = PG_GETARG_INT32(0);
text *fmt = PG_GETARG_TEXT_P(1);
NUMDesc Num;
FormatNode *format;
text *result,
*result_tmp;
int flag = 0;
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_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)));
}
len = strlen(orgnum);
if (*orgnum == '-')
{ /* < 0 */
sign = '-';
--len;
}
else
sign = '+';
if (Num.post)
{
int i;
numstr = palloc(len + 1 + Num.post);
strcpy(numstr, orgnum + (*orgnum == '-' ? 1 : 0));
*(numstr + len) = '.';
for (i = len + 1; i <= Num.post + len + 1; i++)
*(numstr + i) = '0';
*(numstr + Num.post + len + 1) = '\0';
pfree(orgnum);
orgnum = numstr;
}
else
numstr = orgnum + (*orgnum == '-' ? 1 : 0);
if (Num.pre > len)
plen = Num.pre - len;
else if (len > Num.pre)
{
fill_str(numstr, '#', Num.pre);
*(numstr + Num.pre) = '.';
fill_str(numstr + 1 + Num.pre, '#', Num.post);
}
}
NUM_TOCHAR_finish;
PG_RETURN_TEXT_P(result);
}
/* ---------------
* INT8 to_char()
* ---------------
*/
Datum
int8_to_char(PG_FUNCTION_ARGS)
{
int64 value = PG_GETARG_INT64(0);
text *fmt = PG_GETARG_TEXT_P(1);
NUMDesc Num;
FormatNode *format;
text *result,
*result_tmp;
int flag = 0;
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_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)));
len = strlen(orgnum);
if (*orgnum == '-')
{ /* < 0 */
sign = '-';
--len;
}
else
sign = '+';
if (Num.post)
{
int i;
numstr = palloc(len + 1 + Num.post);
strcpy(numstr, orgnum + (*orgnum == '-' ? 1 : 0));
*(numstr + len) = '.';
for (i = len + 1; i <= Num.post + len + 1; i++)
*(numstr + i) = '0';
*(numstr + Num.post + len + 1) = '\0';
pfree(orgnum);
orgnum = numstr;
}
else
numstr = orgnum + (*orgnum == '-' ? 1 : 0);
if (Num.pre > len)
plen = Num.pre - len;
else if (len > Num.pre)
{
fill_str(numstr, '#', Num.pre);
*(numstr + Num.pre) = '.';
fill_str(numstr + 1 + Num.pre, '#', Num.post);
}
}
NUM_TOCHAR_finish;
PG_RETURN_TEXT_P(result);
}
/* -----------------
* FLOAT4 to_char()
* -----------------
*/
Datum
float4_to_char(PG_FUNCTION_ARGS)
{
float4 value = PG_GETARG_FLOAT4(0);
text *fmt = PG_GETARG_TEXT_P(1);
NUMDesc Num;
FormatNode *format;
text *result,
*result_tmp;
int flag = 0;
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
{
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);
len = sprintf(orgnum, "%.0f", fabs(val));
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;
sprintf(orgnum, "%.*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)
{
fill_str(numstr, '#', Num.pre);
*(numstr + Num.pre) = '.';
fill_str(numstr + 1 + Num.pre, '#', Num.post);
}
}
NUM_TOCHAR_finish;
PG_RETURN_TEXT_P(result);
}
/* -----------------
* FLOAT8 to_char()
* -----------------
*/
Datum
float8_to_char(PG_FUNCTION_ARGS)
{
float8 value = PG_GETARG_FLOAT8(0);
text *fmt = PG_GETARG_TEXT_P(1);
NUMDesc Num;
FormatNode *format;
text *result,
*result_tmp;
int flag = 0;
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
{
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 = sprintf(orgnum, "%.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;
sprintf(orgnum, "%.*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)
{
fill_str(numstr, '#', Num.pre);
*(numstr + Num.pre) = '.';
fill_str(numstr + 1 + Num.pre, '#', Num.post);
}
}
NUM_TOCHAR_finish;
PG_RETURN_TEXT_P(result);
}