1 /* -----------------------------------------------------------------------
7 * Portions Copyright (c) 1999-2009, PostgreSQL Global Development Group
10 * TO_CHAR(); TO_TIMESTAMP(); TO_DATE(); TO_NUMBER();
12 * The PostgreSQL routines for a timestamp/int/float/numeric formatting,
13 * inspired by the Oracle TO_CHAR() / TO_DATE() / TO_NUMBER() routines.
17 * Routines use (itself) internal cache for format pictures.
19 * The cache uses a static buffer and is persistent across transactions. If
20 * the format-picture is bigger than the cache buffer, the parser is called
23 * NOTE for Number version:
24 * All in this version is implemented as keywords ( => not used
25 * suffixes), because a format picture is for *one* item (number)
26 * only. It not is as a timestamp version, where each keyword (can)
29 * NOTE for Timestamp routines:
30 * In this module the POSIX 'struct tm' type is *not* used, but rather
31 * PgSQL type, which has tm_mon based on one (*non* zero) and
32 * year *not* based on 1900, but is used full year number.
33 * Module supports AD / BC / AM / PM.
35 * Supported types for to_char():
37 * Timestamp, Numeric, int4, int8, float4, float8
39 * Supported types for reverse conversion:
41 * Timestamp - to_timestamp()
43 * Numeric - to_number()
49 * - better number building (formatting) / parsing, now it isn't
52 * - add support for abstime
53 * - add support for roman number to standard number conversion
54 * - add support for number spelling
55 * - add support for string to string formatting (we must be better
57 * to_char('Hello', 'X X X X X') -> 'H e l l o'
59 * -----------------------------------------------------------------------
62 #ifdef DEBUG_TO_FROM_CHAR
63 #define DEBUG_elog_output DEBUG3
75 * towlower() and friends should be in <wctype.h>, but some pre-C99 systems
76 * declare them in <wchar.h>.
85 #include "utils/builtins.h"
86 #include "utils/date.h"
87 #include "utils/datetime.h"
88 #include "utils/formatting.h"
89 #include "utils/int8.h"
90 #include "utils/numeric.h"
91 #include "utils/pg_locale.h"
92 #include "mb/pg_wchar.h"
98 #define DCH_TYPE 1 /* DATE-TIME version */
99 #define NUM_TYPE 2 /* NUMBER version */
102 * KeyWord Index (ascii from position 32 (' ') to 126 (~))
105 #define KeyWord_INDEX_SIZE ('~' - ' ')
106 #define KeyWord_INDEX_FILTER(_c) ((_c) <= ' ' || (_c) >= '~' ? 0 : 1)
109 * Maximal length of one node
112 #define DCH_MAX_ITEM_SIZ 9 /* max julian day */
113 #define NUM_MAX_ITEM_SIZ 8 /* roman number (RN has 15 chars) */
119 #define MAXFLOATWIDTH 60
120 #define MAXDOUBLEWIDTH 500
124 * External (defined in PgSQL datetime.c (timestamp utils))
127 extern char *months
[], /* month abbreviation */
128 *days
[]; /* full days */
131 * Format parser structs
136 char *name
; /* suffix string */
137 int len
, /* suffix length */
138 id
, /* used in node->suffix */
139 type
; /* prefix / postfix */
146 * This value is used to nominate one of several distinct (and mutually
147 * exclusive) date conventions that a keyword can belong to.
151 FROM_CHAR_DATE_NONE
= 0, /* Value does not affect date mode. */
152 FROM_CHAR_DATE_GREGORIAN
, /* Gregorian (day, month, year) style date */
153 FROM_CHAR_DATE_ISOWEEK
/* ISO 8601 week date */
156 typedef struct FormatNode FormatNode
;
164 FromCharDateMode date_mode
;
169 int type
; /* node type */
170 const KeyWord
*key
; /* if node type is KEYWORD */
171 char character
; /* if node type is CHAR */
172 int suffix
; /* keyword suffix */
175 #define NODE_TYPE_END 1
176 #define NODE_TYPE_ACTION 2
177 #define NODE_TYPE_CHAR 3
179 #define SUFFTYPE_PREFIX 1
180 #define SUFFTYPE_POSTFIX 2
182 #define CLOCK_24_HOUR 0
183 #define CLOCK_12_HOUR 1
190 static char *months_full
[] = {
191 "January", "February", "March", "April", "May", "June", "July",
192 "August", "September", "October", "November", "December", NULL
195 static char *days_short
[] = {
196 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
202 * There is no 0 AD. Years go from 1 BC to 1 AD, so we make it
203 * positive and map year == -1 to year zero, and shift all negative
204 * years up one. For interval years, we just return the year.
206 #define ADJUST_YEAR(year, is_interval) ((is_interval) ? (year) : ((year) <= 0 ? -((year) - 1) : (year)))
208 #define A_D_STR "A.D."
209 #define a_d_STR "a.d."
213 #define B_C_STR "B.C."
214 #define b_c_STR "b.c."
219 * AD / BC strings for seq_search.
221 * These are given in two variants, a long form with periods and a standard
224 * The array is laid out such that matches for AD have an even index, and
225 * matches for BC have an odd index. So the boolean value for BC is given by
226 * taking the array index of the match, modulo 2.
228 static char *adbc_strings
[] = {ad_STR
, bc_STR
, AD_STR
, BC_STR
, NULL
};
229 static char *adbc_strings_long
[] = {a_d_STR
, b_c_STR
, A_D_STR
, B_C_STR
, NULL
};
235 #define A_M_STR "A.M."
236 #define a_m_STR "a.m."
240 #define P_M_STR "P.M."
241 #define p_m_STR "p.m."
246 * AM / PM strings for seq_search.
248 * These are given in two variants, a long form with periods and a standard
251 * The array is laid out such that matches for AM have an even index, and
252 * matches for PM have an odd index. So the boolean value for PM is given by
253 * taking the array index of the match, modulo 2.
255 static char *ampm_strings
[] = {am_STR
, pm_STR
, AM_STR
, PM_STR
, NULL
};
256 static char *ampm_strings_long
[] = {a_m_STR
, p_m_STR
, A_M_STR
, P_M_STR
, NULL
};
259 * Months in roman-numeral
260 * (Must be in reverse order for seq_search (in FROM_CHAR), because
261 * 'VIII' must have higher precedence than 'V')
264 static char *rm_months_upper
[] =
265 {"XII", "XI", "X", "IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I", NULL
};
267 static char *rm_months_lower
[] =
268 {"xii", "xi", "x", "ix", "viii", "vii", "vi", "v", "iv", "iii", "ii", "i", NULL
};
274 static char *rm1
[] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", NULL
};
275 static char *rm10
[] = {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", NULL
};
276 static char *rm100
[] = {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", NULL
};
282 static char *numTH
[] = {"ST", "ND", "RD", "TH", NULL
};
283 static char *numth
[] = {"st", "nd", "rd", "th", NULL
};
289 #define ONE_UPPER 1 /* Name */
290 #define ALL_UPPER 2 /* NAME */
291 #define ALL_LOWER 3 /* name */
295 #define MAX_MONTH_LEN 9
296 #define MAX_MON_LEN 3
297 #define MAX_DAY_LEN 9
305 * Number description struct
310 int pre
, /* (count) numbers before decimal */
311 post
, /* (count) numbers after decimal */
312 lsign
, /* want locales sign */
313 flag
, /* number parameters */
314 pre_lsign_num
, /* tmp value for lsign */
315 multi
, /* multiplier for 'V' */
316 zero_start
, /* position of first zero */
317 zero_end
, /* position of last zero */
318 need_locale
; /* needs it locale */
322 * Flags for NUMBER version
325 #define NUM_F_DECIMAL (1 << 1)
326 #define NUM_F_LDECIMAL (1 << 2)
327 #define NUM_F_ZERO (1 << 3)
328 #define NUM_F_BLANK (1 << 4)
329 #define NUM_F_FILLMODE (1 << 5)
330 #define NUM_F_LSIGN (1 << 6)
331 #define NUM_F_BRACKET (1 << 7)
332 #define NUM_F_MINUS (1 << 8)
333 #define NUM_F_PLUS (1 << 9)
334 #define NUM_F_ROMAN (1 << 10)
335 #define NUM_F_MULTI (1 << 11)
336 #define NUM_F_PLUS_POST (1 << 12)
337 #define NUM_F_MINUS_POST (1 << 13)
339 #define NUM_LSIGN_PRE (-1)
340 #define NUM_LSIGN_POST 1
341 #define NUM_LSIGN_NONE 0
347 #define IS_DECIMAL(_f) ((_f)->flag & NUM_F_DECIMAL)
348 #define IS_LDECIMAL(_f) ((_f)->flag & NUM_F_LDECIMAL)
349 #define IS_ZERO(_f) ((_f)->flag & NUM_F_ZERO)
350 #define IS_BLANK(_f) ((_f)->flag & NUM_F_BLANK)
351 #define IS_FILLMODE(_f) ((_f)->flag & NUM_F_FILLMODE)
352 #define IS_BRACKET(_f) ((_f)->flag & NUM_F_BRACKET)
353 #define IS_MINUS(_f) ((_f)->flag & NUM_F_MINUS)
354 #define IS_LSIGN(_f) ((_f)->flag & NUM_F_LSIGN)
355 #define IS_PLUS(_f) ((_f)->flag & NUM_F_PLUS)
356 #define IS_ROMAN(_f) ((_f)->flag & NUM_F_ROMAN)
357 #define IS_MULTI(_f) ((_f)->flag & NUM_F_MULTI)
360 * Format picture cache
362 * Number part = NUM_CACHE_SIZE * NUM_CACHE_FIELDS
363 * Date-time part = DCH_CACHE_SIZE * DCH_CACHE_FIELDS
367 #define NUM_CACHE_SIZE 64
368 #define NUM_CACHE_FIELDS 16
369 #define DCH_CACHE_SIZE 128
370 #define DCH_CACHE_FIELDS 16
374 FormatNode format
[DCH_CACHE_SIZE
+ 1];
375 char str
[DCH_CACHE_SIZE
+ 1];
381 FormatNode format
[NUM_CACHE_SIZE
+ 1];
382 char str
[NUM_CACHE_SIZE
+ 1];
387 /* global cache for --- date/time part */
388 static DCHCacheEntry DCHCache
[DCH_CACHE_FIELDS
+ 1];
390 static int n_DCHCache
= 0; /* number of entries */
391 static int DCHCounter
= 0;
393 /* global cache for --- number part */
394 static NUMCacheEntry NUMCache
[NUM_CACHE_FIELDS
+ 1];
396 static int n_NUMCache
= 0; /* number of entries */
397 static int NUMCounter
= 0;
398 static NUMCacheEntry
*last_NUMCacheEntry
= NUMCache
+ 0;
401 * For char->date/time conversion
406 FromCharDateMode mode
;
424 yysz
, /* is it YY or YYYY ? */
425 clock
; /* 12 or 24 hour clock? */
428 #define ZERO_tmfc(_X) memset(_X, 0, sizeof(TmFromChar))
434 #ifdef DEBUG_TO_FROM_CHAR
435 #define DEBUG_TMFC(_X) \
436 elog(DEBUG_elog_output, "TMFC:\nmode %d\nhh %d\npm %d\nmi %d\nss %d\nssss %d\nd %d\ndd %d\nddd %d\nmm %d\nms: %d\nyear %d\nbc %d\nww %d\nw %d\ncc %d\nj %d\nus: %d\nyysz: %d\nclock: %d", \
437 (_X)->mode, (_X)->hh, (_X)->pm, (_X)->mi, (_X)->ss, (_X)->ssss, \
438 (_X)->d, (_X)->dd, (_X)->ddd, (_X)->mm, (_X)->ms, (_X)->year, \
439 (_X)->bc, (_X)->ww, (_X)->w, (_X)->cc, (_X)->j, (_X)->us, \
440 (_X)->yysz, (_X)->clock);
441 #define DEBUG_TM(_X) \
442 elog(DEBUG_elog_output, "TM:\nsec %d\nyear %d\nmin %d\nwday %d\nhour %d\nyday %d\nmday %d\nnisdst %d\nmon %d\n",\
443 (_X)->tm_sec, (_X)->tm_year,\
444 (_X)->tm_min, (_X)->tm_wday, (_X)->tm_hour, (_X)->tm_yday,\
445 (_X)->tm_mday, (_X)->tm_isdst, (_X)->tm_mon)
447 #define DEBUG_TMFC(_X)
452 * Datetime to char conversion
455 typedef struct TmToChar
457 struct pg_tm tm
; /* classic 'tm' struct */
458 fsec_t fsec
; /* fractional seconds */
459 char *tzn
; /* timezone */
462 #define tmtcTm(_X) (&(_X)->tm)
463 #define tmtcTzn(_X) ((_X)->tzn)
464 #define tmtcFsec(_X) ((_X)->fsec)
466 #define ZERO_tm(_X) \
468 (_X)->tm_sec = (_X)->tm_year = (_X)->tm_min = (_X)->tm_wday = \
469 (_X)->tm_hour = (_X)->tm_yday = (_X)->tm_isdst = 0; \
470 (_X)->tm_mday = (_X)->tm_mon = 1; \
473 #define ZERO_tmtc(_X) \
475 ZERO_tm( tmtcTm(_X) ); \
477 tmtcTzn(_X) = NULL; \
481 * to_char(time) appears to to_char() as an interval, so this check
482 * is really for interval and time data types.
484 #define INVALID_FOR_INTERVAL \
488 (errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
489 errmsg("invalid format specification for an interval value"), \
490 errhint("Intervals are not tied to specific calendar dates."))); \
493 /*****************************************************************************
494 * KeyWord definitions
495 *****************************************************************************/
501 #define DCH_S_FM 0x01
502 #define DCH_S_TH 0x02
503 #define DCH_S_th 0x04
504 #define DCH_S_SP 0x08
505 #define DCH_S_TM 0x10
511 #define S_THth(_s) ((((_s) & DCH_S_TH) || ((_s) & DCH_S_th)) ? 1 : 0)
512 #define S_TH(_s) (((_s) & DCH_S_TH) ? 1 : 0)
513 #define S_th(_s) (((_s) & DCH_S_th) ? 1 : 0)
514 #define S_TH_TYPE(_s) (((_s) & DCH_S_TH) ? TH_UPPER : TH_LOWER)
516 #define S_FM(_s) (((_s) & DCH_S_FM) ? 1 : 0)
517 #define S_SP(_s) (((_s) & DCH_S_SP) ? 1 : 0)
518 #define S_TM(_s) (((_s) & DCH_S_TM) ? 1 : 0)
521 * Suffixes definition for DATE-TIME TO/FROM CHAR
524 static KeySuffix DCH_suff
[] = {
525 {"FM", 2, DCH_S_FM
, SUFFTYPE_PREFIX
},
526 {"fm", 2, DCH_S_FM
, SUFFTYPE_PREFIX
},
527 {"TM", 2, DCH_S_TM
, SUFFTYPE_PREFIX
},
528 {"tm", 2, DCH_S_TM
, SUFFTYPE_PREFIX
},
529 {"TH", 2, DCH_S_TH
, SUFFTYPE_POSTFIX
},
530 {"th", 2, DCH_S_th
, SUFFTYPE_POSTFIX
},
531 {"SP", 2, DCH_S_SP
, SUFFTYPE_POSTFIX
},
537 * Format-pictures (KeyWord).
539 * The KeyWord field; alphabetic sorted, *BUT* strings alike is sorted
540 * complicated -to-> easy:
542 * (example: "DDD","DD","Day","D" )
544 * (this specific sort needs the algorithm for sequential search for strings,
545 * which not has exact end; -> How keyword is in "HH12blabla" ? - "HH"
546 * or "HH12"? You must first try "HH12", because "HH" is in string, but
550 * - Position for the keyword is similar as position in the enum DCH/NUM_poz.
553 * For fast search is used the 'int index[]', index is ascii table from position
554 * 32 (' ') to 126 (~), in this index is DCH_ / NUM_ enums for each ASCII
555 * position or -1 if char is not used in the KeyWord. Search example for
557 * 1) see in index to index['M' - 32],
558 * 2) take keywords position (enum DCH_MI) from index
559 * 3) run sequential search in keywords[] from this position
580 DCH_FX
, /* global suffix */
707 * KeyWords for DATE-TIME version
710 static const KeyWord DCH_keywords
[] = {
711 /* name, len, id, is_digit, date_mode */
712 {"A.D.", 4, DCH_A_D
, FALSE
, FROM_CHAR_DATE_NONE
}, /* A */
713 {"A.M.", 4, DCH_A_M
, FALSE
, FROM_CHAR_DATE_NONE
},
714 {"AD", 2, DCH_AD
, FALSE
, FROM_CHAR_DATE_NONE
},
715 {"AM", 2, DCH_AM
, FALSE
, FROM_CHAR_DATE_NONE
},
716 {"B.C.", 4, DCH_B_C
, FALSE
, FROM_CHAR_DATE_NONE
}, /* B */
717 {"BC", 2, DCH_BC
, FALSE
, FROM_CHAR_DATE_NONE
},
718 {"CC", 2, DCH_CC
, TRUE
, FROM_CHAR_DATE_NONE
}, /* C */
719 {"DAY", 3, DCH_DAY
, FALSE
, FROM_CHAR_DATE_NONE
}, /* D */
720 {"DDD", 3, DCH_DDD
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
721 {"DD", 2, DCH_DD
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
722 {"DY", 2, DCH_DY
, FALSE
, FROM_CHAR_DATE_NONE
},
723 {"Day", 3, DCH_Day
, FALSE
, FROM_CHAR_DATE_NONE
},
724 {"Dy", 2, DCH_Dy
, FALSE
, FROM_CHAR_DATE_NONE
},
725 {"D", 1, DCH_D
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
726 {"FX", 2, DCH_FX
, FALSE
, FROM_CHAR_DATE_NONE
}, /* F */
727 {"HH24", 4, DCH_HH24
, TRUE
, FROM_CHAR_DATE_NONE
}, /* H */
728 {"HH12", 4, DCH_HH12
, TRUE
, FROM_CHAR_DATE_NONE
},
729 {"HH", 2, DCH_HH
, TRUE
, FROM_CHAR_DATE_NONE
},
730 {"IDDD", 4, DCH_IDDD
, TRUE
, FROM_CHAR_DATE_ISOWEEK
}, /* I */
731 {"ID", 2, DCH_ID
, TRUE
, FROM_CHAR_DATE_ISOWEEK
},
732 {"IW", 2, DCH_IW
, TRUE
, FROM_CHAR_DATE_ISOWEEK
},
733 {"IYYY", 4, DCH_IYYY
, TRUE
, FROM_CHAR_DATE_ISOWEEK
},
734 {"IYY", 3, DCH_IYY
, TRUE
, FROM_CHAR_DATE_ISOWEEK
},
735 {"IY", 2, DCH_IY
, TRUE
, FROM_CHAR_DATE_ISOWEEK
},
736 {"I", 1, DCH_I
, TRUE
, FROM_CHAR_DATE_ISOWEEK
},
737 {"J", 1, DCH_J
, TRUE
, FROM_CHAR_DATE_NONE
}, /* J */
738 {"MI", 2, DCH_MI
, TRUE
, FROM_CHAR_DATE_NONE
}, /* M */
739 {"MM", 2, DCH_MM
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
740 {"MONTH", 5, DCH_MONTH
, FALSE
, FROM_CHAR_DATE_GREGORIAN
},
741 {"MON", 3, DCH_MON
, FALSE
, FROM_CHAR_DATE_GREGORIAN
},
742 {"MS", 2, DCH_MS
, TRUE
, FROM_CHAR_DATE_NONE
},
743 {"Month", 5, DCH_Month
, FALSE
, FROM_CHAR_DATE_GREGORIAN
},
744 {"Mon", 3, DCH_Mon
, FALSE
, FROM_CHAR_DATE_GREGORIAN
},
745 {"P.M.", 4, DCH_P_M
, FALSE
, FROM_CHAR_DATE_NONE
}, /* P */
746 {"PM", 2, DCH_PM
, FALSE
, FROM_CHAR_DATE_NONE
},
747 {"Q", 1, DCH_Q
, TRUE
, FROM_CHAR_DATE_NONE
}, /* Q */
748 {"RM", 2, DCH_RM
, FALSE
, FROM_CHAR_DATE_GREGORIAN
}, /* R */
749 {"SSSS", 4, DCH_SSSS
, TRUE
, FROM_CHAR_DATE_NONE
}, /* S */
750 {"SS", 2, DCH_SS
, TRUE
, FROM_CHAR_DATE_NONE
},
751 {"TZ", 2, DCH_TZ
, FALSE
, FROM_CHAR_DATE_NONE
}, /* T */
752 {"US", 2, DCH_US
, TRUE
, FROM_CHAR_DATE_NONE
}, /* U */
753 {"WW", 2, DCH_WW
, TRUE
, FROM_CHAR_DATE_GREGORIAN
}, /* W */
754 {"W", 1, DCH_W
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
755 {"Y,YYY", 5, DCH_Y_YYY
, TRUE
, FROM_CHAR_DATE_GREGORIAN
}, /* Y */
756 {"YYYY", 4, DCH_YYYY
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
757 {"YYY", 3, DCH_YYY
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
758 {"YY", 2, DCH_YY
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
759 {"Y", 1, DCH_Y
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
760 {"a.d.", 4, DCH_a_d
, FALSE
, FROM_CHAR_DATE_NONE
}, /* a */
761 {"a.m.", 4, DCH_a_m
, FALSE
, FROM_CHAR_DATE_NONE
},
762 {"ad", 2, DCH_ad
, FALSE
, FROM_CHAR_DATE_NONE
},
763 {"am", 2, DCH_am
, FALSE
, FROM_CHAR_DATE_NONE
},
764 {"b.c.", 4, DCH_b_c
, FALSE
, FROM_CHAR_DATE_NONE
}, /* b */
765 {"bc", 2, DCH_bc
, FALSE
, FROM_CHAR_DATE_NONE
},
766 {"cc", 2, DCH_CC
, TRUE
, FROM_CHAR_DATE_NONE
}, /* c */
767 {"day", 3, DCH_day
, FALSE
, FROM_CHAR_DATE_NONE
}, /* d */
768 {"ddd", 3, DCH_DDD
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
769 {"dd", 2, DCH_DD
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
770 {"dy", 2, DCH_dy
, FALSE
, FROM_CHAR_DATE_NONE
},
771 {"d", 1, DCH_D
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
772 {"fx", 2, DCH_FX
, FALSE
, FROM_CHAR_DATE_NONE
}, /* f */
773 {"hh24", 4, DCH_HH24
, TRUE
, FROM_CHAR_DATE_NONE
}, /* h */
774 {"hh12", 4, DCH_HH12
, TRUE
, FROM_CHAR_DATE_NONE
},
775 {"hh", 2, DCH_HH
, TRUE
, FROM_CHAR_DATE_NONE
},
776 {"iddd", 4, DCH_IDDD
, TRUE
, FROM_CHAR_DATE_ISOWEEK
}, /* i */
777 {"id", 2, DCH_ID
, TRUE
, FROM_CHAR_DATE_ISOWEEK
},
778 {"iw", 2, DCH_IW
, TRUE
, FROM_CHAR_DATE_ISOWEEK
},
779 {"iyyy", 4, DCH_IYYY
, TRUE
, FROM_CHAR_DATE_ISOWEEK
},
780 {"iyy", 3, DCH_IYY
, TRUE
, FROM_CHAR_DATE_ISOWEEK
},
781 {"iy", 2, DCH_IY
, TRUE
, FROM_CHAR_DATE_ISOWEEK
},
782 {"i", 1, DCH_I
, TRUE
, FROM_CHAR_DATE_ISOWEEK
},
783 {"j", 1, DCH_J
, TRUE
, FROM_CHAR_DATE_NONE
}, /* j */
784 {"mi", 2, DCH_MI
, TRUE
, FROM_CHAR_DATE_NONE
}, /* m */
785 {"mm", 2, DCH_MM
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
786 {"month", 5, DCH_month
, FALSE
, FROM_CHAR_DATE_GREGORIAN
},
787 {"mon", 3, DCH_mon
, FALSE
, FROM_CHAR_DATE_GREGORIAN
},
788 {"ms", 2, DCH_MS
, TRUE
, FROM_CHAR_DATE_NONE
},
789 {"p.m.", 4, DCH_p_m
, FALSE
, FROM_CHAR_DATE_NONE
}, /* p */
790 {"pm", 2, DCH_pm
, FALSE
, FROM_CHAR_DATE_NONE
},
791 {"q", 1, DCH_Q
, TRUE
, FROM_CHAR_DATE_NONE
}, /* q */
792 {"rm", 2, DCH_rm
, FALSE
, FROM_CHAR_DATE_GREGORIAN
}, /* r */
793 {"ssss", 4, DCH_SSSS
, TRUE
, FROM_CHAR_DATE_NONE
}, /* s */
794 {"ss", 2, DCH_SS
, TRUE
, FROM_CHAR_DATE_NONE
},
795 {"tz", 2, DCH_tz
, FALSE
, FROM_CHAR_DATE_NONE
}, /* t */
796 {"us", 2, DCH_US
, TRUE
, FROM_CHAR_DATE_NONE
}, /* u */
797 {"ww", 2, DCH_WW
, TRUE
, FROM_CHAR_DATE_GREGORIAN
}, /* w */
798 {"w", 1, DCH_W
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
799 {"y,yyy", 5, DCH_Y_YYY
, TRUE
, FROM_CHAR_DATE_GREGORIAN
}, /* y */
800 {"yyyy", 4, DCH_YYYY
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
801 {"yyy", 3, DCH_YYY
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
802 {"yy", 2, DCH_YY
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
803 {"y", 1, DCH_Y
, TRUE
, FROM_CHAR_DATE_GREGORIAN
},
810 * KeyWords for NUMBER version
812 * The is_digit and date_mode fields are not relevant here.
815 static const KeyWord NUM_keywords
[] = {
816 /* name, len, id is in Index */
817 {",", 1, NUM_COMMA
}, /* , */
818 {".", 1, NUM_DEC
}, /* . */
819 {"0", 1, NUM_0
}, /* 0 */
820 {"9", 1, NUM_9
}, /* 9 */
821 {"B", 1, NUM_B
}, /* B */
822 {"C", 1, NUM_C
}, /* C */
823 {"D", 1, NUM_D
}, /* D */
824 {"E", 1, NUM_E
}, /* E */
825 {"FM", 2, NUM_FM
}, /* F */
826 {"G", 1, NUM_G
}, /* G */
827 {"L", 1, NUM_L
}, /* L */
828 {"MI", 2, NUM_MI
}, /* M */
829 {"PL", 2, NUM_PL
}, /* P */
831 {"RN", 2, NUM_RN
}, /* R */
832 {"SG", 2, NUM_SG
}, /* S */
835 {"TH", 2, NUM_TH
}, /* T */
836 {"V", 1, NUM_V
}, /* V */
837 {"b", 1, NUM_B
}, /* b */
838 {"c", 1, NUM_C
}, /* c */
839 {"d", 1, NUM_D
}, /* d */
840 {"e", 1, NUM_E
}, /* e */
841 {"fm", 2, NUM_FM
}, /* f */
842 {"g", 1, NUM_G
}, /* g */
843 {"l", 1, NUM_L
}, /* l */
844 {"mi", 2, NUM_MI
}, /* m */
845 {"pl", 2, NUM_PL
}, /* p */
847 {"rn", 2, NUM_rn
}, /* r */
848 {"sg", 2, NUM_SG
}, /* s */
851 {"th", 2, NUM_th
}, /* t */
852 {"v", 1, NUM_V
}, /* v */
860 * KeyWords index for DATE-TIME version
863 static const int DCH_index
[KeyWord_INDEX_SIZE
] = {
867 /*---- first 0..31 chars are skipped ----*/
869 -1, -1, -1, -1, -1, -1, -1, -1,
870 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
871 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
872 -1, -1, -1, -1, -1, DCH_A_D
, DCH_B_C
, DCH_CC
, DCH_DAY
, -1,
873 DCH_FX
, -1, DCH_HH24
, DCH_IDDD
, DCH_J
, -1, -1, DCH_MI
, -1, -1,
874 DCH_P_M
, DCH_Q
, DCH_RM
, DCH_SSSS
, DCH_TZ
, DCH_US
, -1, DCH_WW
, -1, DCH_Y_YYY
,
875 -1, -1, -1, -1, -1, -1, -1, DCH_a_d
, DCH_b_c
, DCH_cc
,
876 DCH_day
, -1, DCH_fx
, -1, DCH_hh24
, DCH_iddd
, DCH_j
, -1, -1, DCH_mi
,
877 -1, -1, DCH_p_m
, DCH_q
, DCH_rm
, DCH_ssss
, DCH_tz
, DCH_us
, -1, DCH_ww
,
878 -1, DCH_y_yyy
, -1, -1, -1, -1
880 /*---- chars over 126 are skipped ----*/
884 * KeyWords index for NUMBER version
887 static const int NUM_index
[KeyWord_INDEX_SIZE
] = {
891 /*---- first 0..31 chars are skipped ----*/
893 -1, -1, -1, -1, -1, -1, -1, -1,
894 -1, -1, -1, -1, NUM_COMMA
, -1, NUM_DEC
, -1, NUM_0
, -1,
895 -1, -1, -1, -1, -1, -1, -1, NUM_9
, -1, -1,
896 -1, -1, -1, -1, -1, -1, NUM_B
, NUM_C
, NUM_D
, NUM_E
,
897 NUM_FM
, NUM_G
, -1, -1, -1, -1, NUM_L
, NUM_MI
, -1, -1,
898 NUM_PL
, -1, NUM_RN
, NUM_SG
, NUM_TH
, -1, NUM_V
, -1, -1, -1,
899 -1, -1, -1, -1, -1, -1, -1, -1, NUM_b
, NUM_c
,
900 NUM_d
, NUM_e
, NUM_fm
, NUM_g
, -1, -1, -1, -1, NUM_l
, NUM_mi
,
901 -1, -1, NUM_pl
, -1, NUM_rn
, NUM_sg
, NUM_th
, -1, NUM_v
, -1,
902 -1, -1, -1, -1, -1, -1
904 /*---- chars over 126 are skipped ----*/
908 * Number processor struct
911 typedef struct NUMProc
914 NUMDesc
*Num
; /* number description */
916 int sign
, /* '-' or '+' */
917 sign_wrote
, /* was sign write */
918 num_count
, /* number of write digits */
919 num_in
, /* is inside number */
920 num_curr
, /* current position in number */
921 num_pre
, /* space before first number */
923 read_dec
, /* to_number - was read dec. point */
924 read_post
, /* to_number - number of dec. digit */
925 read_pre
; /* to_number - number non-dec. digit */
927 char *number
, /* string with number */
928 *number_p
, /* pointer to current number position */
929 *inout
, /* in / out buffer */
930 *inout_p
, /* pointer to current inout position */
931 *last_relevant
, /* last relevant number after decimal point */
933 *L_negative_sign
, /* Locale */
945 static const KeyWord
*index_seq_search(char *str
, const KeyWord
*kw
,
947 static KeySuffix
*suff_search(char *str
, KeySuffix
*suf
, int type
);
948 static void NUMDesc_prepare(NUMDesc
*num
, FormatNode
*n
);
949 static void parse_format(FormatNode
*node
, char *str
, const KeyWord
*kw
,
950 KeySuffix
*suf
, const int *index
, int ver
, NUMDesc
*Num
);
952 static void DCH_to_char(FormatNode
*node
, bool is_interval
,
953 TmToChar
*in
, char *out
);
954 static void DCH_from_char(FormatNode
*node
, char *in
, TmFromChar
*out
);
956 #ifdef DEBUG_TO_FROM_CHAR
957 static void dump_index(const KeyWord
*k
, const int *index
);
958 static void dump_node(FormatNode
*node
, int max
);
961 static char *get_th(char *num
, int type
);
962 static char *str_numth(char *dest
, char *num
, int type
);
963 static int strspace_len(char *str
);
964 static int strdigits_len(char *str
);
965 static void from_char_set_mode(TmFromChar
*tmfc
, const FromCharDateMode mode
);
966 static void from_char_set_int(int *dest
, const int value
, const FormatNode
*node
);
967 static int from_char_parse_int_len(int *dest
, char **src
, const int len
, FormatNode
*node
);
968 static int from_char_parse_int(int *dest
, char **src
, FormatNode
*node
);
969 static int seq_search(char *name
, char **array
, int type
, int max
, int *len
);
970 static int from_char_seq_search(int *dest
, char **src
, char **array
, int type
, int max
, FormatNode
*node
);
971 static void do_to_timestamp(text
*date_txt
, text
*fmt
,
972 struct pg_tm
* tm
, fsec_t
*fsec
);
973 static char *fill_str(char *str
, int c
, int max
);
974 static FormatNode
*NUM_cache(int len
, NUMDesc
*Num
, text
*pars_str
, bool *shouldFree
);
975 static char *int_to_roman(int number
);
976 static void NUM_prepare_locale(NUMProc
*Np
);
977 static char *get_last_relevant_decnum(char *num
);
978 static void NUM_numpart_from_char(NUMProc
*Np
, int id
, int plen
);
979 static void NUM_numpart_to_char(NUMProc
*Np
, int id
);
980 static char *NUM_processor(FormatNode
*node
, NUMDesc
*Num
, char *inout
, char *number
,
981 int plen
, int sign
, bool is_to_char
);
982 static DCHCacheEntry
*DCH_cache_search(char *str
);
983 static DCHCacheEntry
*DCH_cache_getnew(char *str
);
985 static NUMCacheEntry
*NUM_cache_search(char *str
);
986 static NUMCacheEntry
*NUM_cache_getnew(char *str
);
987 static void NUM_cache_remove(NUMCacheEntry
*ent
);
991 * Fast sequential search, use index for data selection which
992 * go to seq. cycle (it is very fast for unwanted strings)
993 * (can't be used binary search in format parsing)
996 static const KeyWord
*
997 index_seq_search(char *str
, const KeyWord
*kw
, const int *index
)
1001 if (!KeyWord_INDEX_FILTER(*str
))
1004 if ((poz
= *(index
+ (*str
- ' '))) > -1)
1006 const KeyWord
*k
= kw
+ poz
;
1010 if (!strncmp(str
, k
->name
, k
->len
))
1015 } while (*str
== *k
->name
);
1021 suff_search(char *str
, KeySuffix
*suf
, int type
)
1025 for (s
= suf
; s
->name
!= NULL
; s
++)
1027 if (s
->type
!= type
)
1030 if (!strncmp(str
, s
->name
, s
->len
))
1037 * Prepare NUMDesc (number description struct) via FormatNode struct
1041 NUMDesc_prepare(NUMDesc
*num
, FormatNode
*n
)
1044 if (n
->type
!= NODE_TYPE_ACTION
)
1050 if (IS_BRACKET(num
))
1052 NUM_cache_remove(last_NUMCacheEntry
);
1054 (errcode(ERRCODE_SYNTAX_ERROR
),
1055 errmsg("\"9\" must be ahead of \"PR\"")));
1062 if (IS_DECIMAL(num
))
1069 if (IS_BRACKET(num
))
1071 NUM_cache_remove(last_NUMCacheEntry
);
1073 (errcode(ERRCODE_SYNTAX_ERROR
),
1074 errmsg("\"0\" must be ahead of \"PR\"")));
1076 if (!IS_ZERO(num
) && !IS_DECIMAL(num
))
1078 num
->flag
|= NUM_F_ZERO
;
1079 num
->zero_start
= num
->pre
+ 1;
1081 if (!IS_DECIMAL(num
))
1086 num
->zero_end
= num
->pre
+ num
->post
;
1090 if (num
->pre
== 0 && num
->post
== 0 && (!IS_ZERO(num
)))
1091 num
->flag
|= NUM_F_BLANK
;
1095 num
->flag
|= NUM_F_LDECIMAL
;
1096 num
->need_locale
= TRUE
;
1098 if (IS_DECIMAL(num
))
1100 NUM_cache_remove(last_NUMCacheEntry
);
1102 (errcode(ERRCODE_SYNTAX_ERROR
),
1103 errmsg("multiple decimal points")));
1107 NUM_cache_remove(last_NUMCacheEntry
);
1109 (errcode(ERRCODE_SYNTAX_ERROR
),
1110 errmsg("cannot use \"V\" and decimal point together")));
1112 num
->flag
|= NUM_F_DECIMAL
;
1116 num
->flag
|= NUM_F_FILLMODE
;
1122 NUM_cache_remove(last_NUMCacheEntry
);
1124 (errcode(ERRCODE_SYNTAX_ERROR
),
1125 errmsg("cannot use \"S\" twice")));
1127 if (IS_PLUS(num
) || IS_MINUS(num
) || IS_BRACKET(num
))
1129 NUM_cache_remove(last_NUMCacheEntry
);
1131 (errcode(ERRCODE_SYNTAX_ERROR
),
1132 errmsg("cannot use \"S\" and \"PL\"/\"MI\"/\"SG\"/\"PR\" together")));
1134 if (!IS_DECIMAL(num
))
1136 num
->lsign
= NUM_LSIGN_PRE
;
1137 num
->pre_lsign_num
= num
->pre
;
1138 num
->need_locale
= TRUE
;
1139 num
->flag
|= NUM_F_LSIGN
;
1141 else if (num
->lsign
== NUM_LSIGN_NONE
)
1143 num
->lsign
= NUM_LSIGN_POST
;
1144 num
->need_locale
= TRUE
;
1145 num
->flag
|= NUM_F_LSIGN
;
1152 NUM_cache_remove(last_NUMCacheEntry
);
1154 (errcode(ERRCODE_SYNTAX_ERROR
),
1155 errmsg("cannot use \"S\" and \"MI\" together")));
1157 num
->flag
|= NUM_F_MINUS
;
1158 if (IS_DECIMAL(num
))
1159 num
->flag
|= NUM_F_MINUS_POST
;
1165 NUM_cache_remove(last_NUMCacheEntry
);
1167 (errcode(ERRCODE_SYNTAX_ERROR
),
1168 errmsg("cannot use \"S\" and \"PL\" together")));
1170 num
->flag
|= NUM_F_PLUS
;
1171 if (IS_DECIMAL(num
))
1172 num
->flag
|= NUM_F_PLUS_POST
;
1178 NUM_cache_remove(last_NUMCacheEntry
);
1180 (errcode(ERRCODE_SYNTAX_ERROR
),
1181 errmsg("cannot use \"S\" and \"SG\" together")));
1183 num
->flag
|= NUM_F_MINUS
;
1184 num
->flag
|= NUM_F_PLUS
;
1188 if (IS_LSIGN(num
) || IS_PLUS(num
) || IS_MINUS(num
))
1190 NUM_cache_remove(last_NUMCacheEntry
);
1192 (errcode(ERRCODE_SYNTAX_ERROR
),
1193 errmsg("cannot use \"PR\" and \"S\"/\"PL\"/\"MI\"/\"SG\" together")));
1195 num
->flag
|= NUM_F_BRACKET
;
1200 num
->flag
|= NUM_F_ROMAN
;
1205 num
->need_locale
= TRUE
;
1209 if (IS_DECIMAL(num
))
1211 NUM_cache_remove(last_NUMCacheEntry
);
1213 (errcode(ERRCODE_SYNTAX_ERROR
),
1214 errmsg("cannot use \"V\" and decimal point together")));
1216 num
->flag
|= NUM_F_MULTI
;
1220 NUM_cache_remove(last_NUMCacheEntry
);
1222 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1223 errmsg("\"E\" is not supported")));
1230 * Format parser, search small keywords and keyword's suffixes, and make
1233 * for DATE-TIME & NUMBER version
1237 parse_format(FormatNode
*node
, char *str
, const KeyWord
*kw
,
1238 KeySuffix
*suf
, const int *index
, int ver
, NUMDesc
*Num
)
1246 #ifdef DEBUG_TO_FROM_CHAR
1247 elog(DEBUG_elog_output
, "to_char/number(): run parser");
1259 if (ver
== DCH_TYPE
&& (s
= suff_search(str
, suf
, SUFFTYPE_PREFIX
)) != NULL
)
1269 if (*str
&& (n
->key
= index_seq_search(str
, kw
, index
)) != NULL
)
1271 n
->type
= NODE_TYPE_ACTION
;
1278 * NUM version: Prepare global NUMDesc struct
1280 if (ver
== NUM_TYPE
)
1281 NUMDesc_prepare(Num
, n
);
1286 if (ver
== DCH_TYPE
&& *str
&& (s
= suff_search(str
, suf
, SUFFTYPE_POSTFIX
)) != NULL
)
1296 * Special characters '\' and '"'
1298 if (*str
== '"' && last
!= '\\')
1304 if (*str
== '"' && x
!= '\\')
1309 else if (*str
== '\\' && x
!= '\\')
1314 n
->type
= NODE_TYPE_CHAR
;
1315 n
->character
= *str
;
1325 else if (*str
&& *str
== '\\' && last
!= '\\' && *(str
+ 1) == '"')
1332 n
->type
= NODE_TYPE_CHAR
;
1333 n
->character
= *str
;
1344 if (n
->type
== NODE_TYPE_ACTION
)
1353 n
->type
= NODE_TYPE_END
;
1359 * DEBUG: Dump the FormatNode Tree (debug)
1362 #ifdef DEBUG_TO_FROM_CHAR
1364 #define DUMP_THth(_suf) (S_TH(_suf) ? "TH" : (S_th(_suf) ? "th" : " "))
1365 #define DUMP_FM(_suf) (S_FM(_suf) ? "FM" : " ")
1368 dump_node(FormatNode
*node
, int max
)
1373 elog(DEBUG_elog_output
, "to_from-char(): DUMP FORMAT");
1375 for (a
= 0, n
= node
; a
<= max
; n
++, a
++)
1377 if (n
->type
== NODE_TYPE_ACTION
)
1378 elog(DEBUG_elog_output
, "%d:\t NODE_TYPE_ACTION '%s'\t(%s,%s)",
1379 a
, n
->key
->name
, DUMP_THth(n
->suffix
), DUMP_FM(n
->suffix
));
1380 else if (n
->type
== NODE_TYPE_CHAR
)
1381 elog(DEBUG_elog_output
, "%d:\t NODE_TYPE_CHAR '%c'", a
, n
->character
);
1382 else if (n
->type
== NODE_TYPE_END
)
1384 elog(DEBUG_elog_output
, "%d:\t NODE_TYPE_END", a
);
1388 elog(DEBUG_elog_output
, "%d:\t unknown NODE!", a
);
1393 /*****************************************************************************
1395 *****************************************************************************/
1398 * Return ST/ND/RD/TH for simple (1..9) numbers
1399 * type --> 0 upper, 1 lower
1403 get_th(char *num
, int type
)
1405 int len
= strlen(num
),
1409 last
= *(num
+ (len
- 1));
1410 if (!isdigit((unsigned char) last
))
1412 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION
),
1413 errmsg("\"%s\" is not a number", num
)));
1416 * All "teens" (<x>1[0-9]) get 'TH/th', while <x>[02-9][123] still get
1417 * 'ST/st', 'ND/nd', 'RD/rd', respectively
1419 if ((len
> 1) && ((seclast
= num
[len
- 2]) == '1'))
1425 if (type
== TH_UPPER
)
1429 if (type
== TH_UPPER
)
1433 if (type
== TH_UPPER
)
1437 if (type
== TH_UPPER
)
1445 * Convert string-number to ordinal string-number
1446 * type --> 0 upper, 1 lower
1450 str_numth(char *dest
, char *num
, int type
)
1454 strcat(dest
, get_th(num
, type
));
1459 * If the system provides the needed functions for wide-character manipulation
1460 * (which are all standardized by C99), then we implement upper/lower/initcap
1461 * using wide-character functions, if necessary. Otherwise we use the
1462 * traditional <ctype.h> functions, which of course will not work as desired
1463 * in multibyte character sets. Note that in either case we are effectively
1464 * assuming that the database character encoding matches the encoding implied
1469 * wide-character-aware lower function
1471 * We pass the number of bytes so we can pass varlena and char*
1472 * to this function. The result is a palloc'd, null-terminated string.
1475 str_tolower(const char *buff
, size_t nbytes
)
1482 #ifdef USE_WIDE_UPPER_LOWER
1483 if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
1489 /* Overflow paranoia */
1490 if ((nbytes
+ 1) > (INT_MAX
/ sizeof(wchar_t)))
1492 (errcode(ERRCODE_OUT_OF_MEMORY
),
1493 errmsg("out of memory")));
1495 /* Output workspace cannot have more codes than input bytes */
1496 workspace
= (wchar_t *) palloc((nbytes
+ 1) * sizeof(wchar_t));
1498 char2wchar(workspace
, nbytes
+ 1, buff
, nbytes
);
1500 for (curr_char
= 0; workspace
[curr_char
] != 0; curr_char
++)
1501 workspace
[curr_char
] = towlower(workspace
[curr_char
]);
1503 /* Make result large enough; case change might change number of bytes */
1504 result_size
= curr_char
* pg_database_encoding_max_length() + 1;
1505 result
= palloc(result_size
);
1507 wchar2char(result
, workspace
, result_size
);
1511 #endif /* USE_WIDE_UPPER_LOWER */
1515 result
= pnstrdup(buff
, nbytes
);
1517 for (p
= result
; *p
; p
++)
1518 *p
= pg_tolower((unsigned char) *p
);
1525 * wide-character-aware upper function
1527 * We pass the number of bytes so we can pass varlena and char*
1528 * to this function. The result is a palloc'd, null-terminated string.
1531 str_toupper(const char *buff
, size_t nbytes
)
1538 #ifdef USE_WIDE_UPPER_LOWER
1539 if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
1545 /* Overflow paranoia */
1546 if ((nbytes
+ 1) > (INT_MAX
/ sizeof(wchar_t)))
1548 (errcode(ERRCODE_OUT_OF_MEMORY
),
1549 errmsg("out of memory")));
1551 /* Output workspace cannot have more codes than input bytes */
1552 workspace
= (wchar_t *) palloc((nbytes
+ 1) * sizeof(wchar_t));
1554 char2wchar(workspace
, nbytes
+ 1, buff
, nbytes
);
1556 for (curr_char
= 0; workspace
[curr_char
] != 0; curr_char
++)
1557 workspace
[curr_char
] = towupper(workspace
[curr_char
]);
1559 /* Make result large enough; case change might change number of bytes */
1560 result_size
= curr_char
* pg_database_encoding_max_length() + 1;
1561 result
= palloc(result_size
);
1563 wchar2char(result
, workspace
, result_size
);
1567 #endif /* USE_WIDE_UPPER_LOWER */
1571 result
= pnstrdup(buff
, nbytes
);
1573 for (p
= result
; *p
; p
++)
1574 *p
= pg_toupper((unsigned char) *p
);
1581 * wide-character-aware initcap function
1583 * We pass the number of bytes so we can pass varlena and char*
1584 * to this function. The result is a palloc'd, null-terminated string.
1587 str_initcap(const char *buff
, size_t nbytes
)
1590 int wasalnum
= false;
1595 #ifdef USE_WIDE_UPPER_LOWER
1596 if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
1602 /* Overflow paranoia */
1603 if ((nbytes
+ 1) > (INT_MAX
/ sizeof(wchar_t)))
1605 (errcode(ERRCODE_OUT_OF_MEMORY
),
1606 errmsg("out of memory")));
1608 /* Output workspace cannot have more codes than input bytes */
1609 workspace
= (wchar_t *) palloc((nbytes
+ 1) * sizeof(wchar_t));
1611 char2wchar(workspace
, nbytes
+ 1, buff
, nbytes
);
1613 for (curr_char
= 0; workspace
[curr_char
] != 0; curr_char
++)
1616 workspace
[curr_char
] = towlower(workspace
[curr_char
]);
1618 workspace
[curr_char
] = towupper(workspace
[curr_char
]);
1619 wasalnum
= iswalnum(workspace
[curr_char
]);
1622 /* Make result large enough; case change might change number of bytes */
1623 result_size
= curr_char
* pg_database_encoding_max_length() + 1;
1624 result
= palloc(result_size
);
1626 wchar2char(result
, workspace
, result_size
);
1630 #endif /* USE_WIDE_UPPER_LOWER */
1634 result
= pnstrdup(buff
, nbytes
);
1636 for (p
= result
; *p
; p
++)
1639 *p
= pg_tolower((unsigned char) *p
);
1641 *p
= pg_toupper((unsigned char) *p
);
1642 wasalnum
= isalnum((unsigned char) *p
);
1649 /* convenience routines for when the input is null-terminated */
1652 str_tolower_z(const char *buff
)
1654 return str_tolower(buff
, strlen(buff
));
1658 str_toupper_z(const char *buff
)
1660 return str_toupper(buff
, strlen(buff
));
1664 str_initcap_z(const char *buff
)
1666 return str_initcap(buff
, strlen(buff
));
1671 * Skip TM / th in FROM_CHAR
1674 #define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0)
1676 #ifdef DEBUG_TO_FROM_CHAR
1678 * DEBUG: Call for debug and for index checking; (Show ASCII char
1679 * and defined keyword for each used position
1683 dump_index(const KeyWord
*k
, const int *index
)
1689 elog(DEBUG_elog_output
, "TO-FROM_CHAR: Dump KeyWord Index:");
1691 for (i
= 0; i
< KeyWord_INDEX_SIZE
; i
++)
1695 elog(DEBUG_elog_output
, "\t%c: %s, ", i
+ 32, k
[index
[i
]].name
);
1701 elog(DEBUG_elog_output
, "\t(%d) %c %d", i
, i
+ 32, index
[i
]);
1704 elog(DEBUG_elog_output
, "\n\t\tUsed positions: %d,\n\t\tFree positions: %d",
1710 * Return TRUE if next format picture is not digit value
1714 is_next_separator(FormatNode
*n
)
1716 if (n
->type
== NODE_TYPE_END
)
1719 if (n
->type
== NODE_TYPE_ACTION
&& S_THth(n
->suffix
))
1727 /* end of format string is treated like a non-digit separator */
1728 if (n
->type
== NODE_TYPE_END
)
1731 if (n
->type
== NODE_TYPE_ACTION
)
1733 if (n
->key
->is_digit
)
1738 else if (isdigit((unsigned char) n
->character
))
1741 return TRUE
; /* some non-digit input (separator) */
1745 strspace_len(char *str
)
1749 while (*str
&& isspace((unsigned char) *str
))
1758 strdigits_len(char *str
)
1763 len
= strspace_len(str
);
1766 while (*p
&& isdigit((unsigned char) *p
) && len
<= DCH_MAX_ITEM_SIZ
)
1775 * Set the date mode of a from-char conversion.
1777 * Puke if the date mode has already been set, and the caller attempts to set
1778 * it to a conflicting mode.
1781 from_char_set_mode(TmFromChar
*tmfc
, const FromCharDateMode mode
)
1783 if (mode
!= FROM_CHAR_DATE_NONE
)
1785 if (tmfc
->mode
== FROM_CHAR_DATE_NONE
)
1787 else if (tmfc
->mode
!= mode
)
1789 (errcode(ERRCODE_INVALID_DATETIME_FORMAT
),
1790 errmsg("invalid combination of date conventions"),
1791 errhint("Do not mix Gregorian and ISO week date "
1792 "conventions in a formatting template.")));
1797 * Set the integer pointed to by 'dest' to the given value.
1799 * Puke if the destination integer has previously been set to some other
1803 from_char_set_int(int *dest
, const int value
, const FormatNode
*node
)
1805 if (*dest
!= 0 && *dest
!= value
)
1807 (errcode(ERRCODE_INVALID_DATETIME_FORMAT
),
1808 errmsg("conflicting values for \"%s\" field in formatting string",
1810 errdetail("This value contradicts a previous setting for "
1811 "the same field type.")));
1816 * Read a single integer from the source string, into the int pointed to by
1817 * 'dest'. If 'dest' is NULL, the result is discarded.
1819 * In fixed-width mode (the node does not have the FM suffix), consume at most
1820 * 'len' characters. However, any leading whitespace isn't counted in 'len'.
1822 * We use strtol() to recover the integer value from the source string, in
1823 * accordance with the given FormatNode.
1825 * If the conversion completes successfully, src will have been advanced to
1826 * point at the character immediately following the last character used in the
1829 * Return the number of characters consumed.
1831 * Note that from_char_parse_int() provides a more convenient wrapper where
1832 * the length of the field is the same as the length of the format keyword (as
1836 from_char_parse_int_len(int *dest
, char **src
, const int len
, FormatNode
*node
)
1839 char copy
[DCH_MAX_ITEM_SIZ
+ 1];
1844 * Skip any whitespace before parsing the integer.
1846 *src
+= strspace_len(*src
);
1848 Assert(len
<= DCH_MAX_ITEM_SIZ
);
1849 used
= (int) strlcpy(copy
, *src
, len
+ 1);
1851 if (S_FM(node
->suffix
) || is_next_separator(node
))
1854 * This node is in Fill Mode, or the next node is known to be a
1855 * non-digit value, so we just slurp as many characters as we can get.
1858 result
= strtol(init
, src
, 10);
1863 * We need to pull exactly the number of characters given in 'len' out
1864 * of the string, and convert those.
1870 (errcode(ERRCODE_INVALID_DATETIME_FORMAT
),
1871 errmsg("source string too short for \"%s\" formatting field",
1873 errdetail("Field requires %d characters, but only %d "
1876 errhint("If your source string is not fixed-width, try "
1877 "using the \"FM\" modifier.")));
1880 result
= strtol(copy
, &last
, 10);
1883 if (used
> 0 && used
< len
)
1885 (errcode(ERRCODE_INVALID_DATETIME_FORMAT
),
1886 errmsg("invalid value \"%s\" for \"%s\"",
1887 copy
, node
->key
->name
),
1888 errdetail("Field requires %d characters, but only %d "
1889 "could be parsed.", len
, used
),
1890 errhint("If your source string is not fixed-width, try "
1891 "using the \"FM\" modifier.")));
1898 (errcode(ERRCODE_INVALID_DATETIME_FORMAT
),
1899 errmsg("invalid value \"%s\" for \"%s\"",
1900 copy
, node
->key
->name
),
1901 errdetail("Value must be an integer.")));
1903 if (errno
== ERANGE
|| result
< INT_MIN
|| result
> INT_MAX
)
1905 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
1906 errmsg("value for \"%s\" in source string is out of range",
1908 errdetail("Value must be in the range %d to %d.",
1909 INT_MIN
, INT_MAX
)));
1912 from_char_set_int(dest
, (int) result
, node
);
1917 * Call from_char_parse_int_len(), using the length of the format keyword as
1918 * the expected length of the field.
1920 * Don't call this function if the field differs in length from the format
1921 * keyword (as with HH24; the keyword length is 4, but the field length is 2).
1922 * In such cases, call from_char_parse_int_len() instead to specify the
1923 * required length explictly.
1926 from_char_parse_int(int *dest
, char **src
, FormatNode
*node
)
1928 return from_char_parse_int_len(dest
, src
, node
->key
->len
, node
);
1932 * Sequential search with to upper/lower conversion
1936 seq_search(char *name
, char **array
, int type
, int max
, int *len
)
1949 /* set first char */
1950 if (type
== ONE_UPPER
|| type
== ALL_UPPER
)
1951 *name
= pg_toupper((unsigned char) *name
);
1952 else if (type
== ALL_LOWER
)
1953 *name
= pg_tolower((unsigned char) *name
);
1955 for (last
= 0, a
= array
; *a
!= NULL
; a
++)
1957 /* comperate first chars */
1961 for (i
= 1, p
= *a
+ 1, n
= name
+ 1;; n
++, p
++, i
++)
1963 /* search fragment (max) only */
1964 if (max
&& i
== max
)
1975 /* Not found in array 'a' */
1980 * Convert (but convert new chars only)
1984 if (type
== ONE_UPPER
|| type
== ALL_LOWER
)
1985 *n
= pg_tolower((unsigned char) *n
);
1986 else if (type
== ALL_UPPER
)
1987 *n
= pg_toupper((unsigned char) *n
);
1991 #ifdef DEBUG_TO_FROM_CHAR
1992 elog(DEBUG_elog_output
, "N: %c, P: %c, A: %s (%s)",
2004 * Perform a sequential search in 'array' for text matching the first 'max'
2005 * characters of the source string.
2007 * If a match is found, copy the array index of the match into the integer
2008 * pointed to by 'dest', advance 'src' to the end of the part of the string
2009 * which matched, and return the number of characters consumed.
2011 * If the string doesn't match, throw an error.
2014 from_char_seq_search(int *dest
, char **src
, char **array
, int type
, int max
,
2019 *dest
= seq_search(*src
, array
, type
, max
, &len
);
2022 char copy
[DCH_MAX_ITEM_SIZ
+ 1];
2024 Assert(max
<= DCH_MAX_ITEM_SIZ
);
2025 strlcpy(copy
, *src
, max
+ 1);
2028 (errcode(ERRCODE_INVALID_DATETIME_FORMAT
),
2029 errmsg("invalid value \"%s\" for \"%s\"",
2030 copy
, node
->key
->name
),
2031 errdetail("The given value did not match any of the allowed "
2032 "values for this field.")));
2039 * Process a TmToChar struct as denoted by a list of FormatNodes.
2040 * The formatted data is written to the string pointed to by 'out'.
2044 DCH_to_char(FormatNode
*node
, bool is_interval
, TmToChar
*in
, char *out
)
2048 struct pg_tm
*tm
= &in
->tm
;
2049 char buff
[DCH_CACHE_SIZE
];
2052 /* cache localized days and months */
2053 cache_locale_time();
2056 for (n
= node
; n
->type
!= NODE_TYPE_END
; n
++)
2058 if (n
->type
!= NODE_TYPE_ACTION
)
2069 strcpy(s
, (tm
->tm_hour
% HOURS_PER_DAY
>= HOURS_PER_DAY
/ 2)
2070 ? P_M_STR
: A_M_STR
);
2075 strcpy(s
, (tm
->tm_hour
% HOURS_PER_DAY
>= HOURS_PER_DAY
/ 2)
2081 strcpy(s
, (tm
->tm_hour
% HOURS_PER_DAY
>= HOURS_PER_DAY
/ 2)
2082 ? p_m_STR
: a_m_STR
);
2087 strcpy(s
, (tm
->tm_hour
% HOURS_PER_DAY
>= HOURS_PER_DAY
/ 2)
2093 sprintf(s
, "%0*d", S_FM(n
->suffix
) ? 0 : 2,
2094 tm
->tm_hour
% (HOURS_PER_DAY
/ 2) == 0 ? 12 :
2095 tm
->tm_hour
% (HOURS_PER_DAY
/ 2));
2096 if (S_THth(n
->suffix
))
2097 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2101 sprintf(s
, "%0*d", S_FM(n
->suffix
) ? 0 : 2, tm
->tm_hour
);
2102 if (S_THth(n
->suffix
))
2103 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2107 sprintf(s
, "%0*d", S_FM(n
->suffix
) ? 0 : 2, tm
->tm_min
);
2108 if (S_THth(n
->suffix
))
2109 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2113 sprintf(s
, "%0*d", S_FM(n
->suffix
) ? 0 : 2, tm
->tm_sec
);
2114 if (S_THth(n
->suffix
))
2115 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2118 case DCH_MS
: /* millisecond */
2119 #ifdef HAVE_INT64_TIMESTAMP
2120 sprintf(s
, "%03d", (int) (in
->fsec
/ INT64CONST(1000)));
2122 /* No rint() because we can't overflow and we might print US */
2123 sprintf(s
, "%03d", (int) (in
->fsec
* 1000));
2125 if (S_THth(n
->suffix
))
2126 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2129 case DCH_US
: /* microsecond */
2130 #ifdef HAVE_INT64_TIMESTAMP
2131 sprintf(s
, "%06d", (int) in
->fsec
);
2133 /* don't use rint() because we can't overflow 1000 */
2134 sprintf(s
, "%06d", (int) (in
->fsec
* 1000000));
2136 if (S_THth(n
->suffix
))
2137 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2141 sprintf(s
, "%d", tm
->tm_hour
* SECS_PER_HOUR
+
2142 tm
->tm_min
* SECS_PER_MINUTE
+
2144 if (S_THth(n
->suffix
))
2145 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2149 INVALID_FOR_INTERVAL
;
2152 char *p
= str_tolower_z(tmtcTzn(in
));
2160 INVALID_FOR_INTERVAL
;
2163 strcpy(s
, tmtcTzn(in
));
2169 INVALID_FOR_INTERVAL
;
2170 strcpy(s
, (tm
->tm_year
<= 0 ? B_C_STR
: A_D_STR
));
2175 INVALID_FOR_INTERVAL
;
2176 strcpy(s
, (tm
->tm_year
<= 0 ? BC_STR
: AD_STR
));
2181 INVALID_FOR_INTERVAL
;
2182 strcpy(s
, (tm
->tm_year
<= 0 ? b_c_STR
: a_d_STR
));
2187 INVALID_FOR_INTERVAL
;
2188 strcpy(s
, (tm
->tm_year
<= 0 ? bc_STR
: ad_STR
));
2192 INVALID_FOR_INTERVAL
;
2195 if (S_TM(n
->suffix
))
2196 strcpy(s
, str_toupper_z(localized_full_months
[tm
->tm_mon
- 1]));
2198 sprintf(s
, "%*s", S_FM(n
->suffix
) ? 0 : -9,
2199 str_toupper_z(months_full
[tm
->tm_mon
- 1]));
2203 INVALID_FOR_INTERVAL
;
2206 if (S_TM(n
->suffix
))
2207 strcpy(s
, str_initcap_z(localized_full_months
[tm
->tm_mon
- 1]));
2209 sprintf(s
, "%*s", S_FM(n
->suffix
) ? 0 : -9, months_full
[tm
->tm_mon
- 1]);
2213 INVALID_FOR_INTERVAL
;
2216 if (S_TM(n
->suffix
))
2217 strcpy(s
, str_tolower_z(localized_full_months
[tm
->tm_mon
- 1]));
2220 sprintf(s
, "%*s", S_FM(n
->suffix
) ? 0 : -9, months_full
[tm
->tm_mon
- 1]);
2221 *s
= pg_tolower((unsigned char) *s
);
2226 INVALID_FOR_INTERVAL
;
2229 if (S_TM(n
->suffix
))
2230 strcpy(s
, str_toupper_z(localized_abbrev_months
[tm
->tm_mon
- 1]));
2232 strcpy(s
, str_toupper_z(months
[tm
->tm_mon
- 1]));
2236 INVALID_FOR_INTERVAL
;
2239 if (S_TM(n
->suffix
))
2240 strcpy(s
, str_initcap_z(localized_abbrev_months
[tm
->tm_mon
- 1]));
2242 strcpy(s
, months
[tm
->tm_mon
- 1]);
2246 INVALID_FOR_INTERVAL
;
2249 if (S_TM(n
->suffix
))
2250 strcpy(s
, str_tolower_z(localized_abbrev_months
[tm
->tm_mon
- 1]));
2253 strcpy(s
, months
[tm
->tm_mon
- 1]);
2254 *s
= pg_tolower((unsigned char) *s
);
2259 sprintf(s
, "%0*d", S_FM(n
->suffix
) ? 0 : 2, tm
->tm_mon
);
2260 if (S_THth(n
->suffix
))
2261 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2265 INVALID_FOR_INTERVAL
;
2266 if (S_TM(n
->suffix
))
2267 strcpy(s
, str_toupper_z(localized_full_days
[tm
->tm_wday
]));
2269 sprintf(s
, "%*s", S_FM(n
->suffix
) ? 0 : -9,
2270 str_toupper_z(days
[tm
->tm_wday
]));
2274 INVALID_FOR_INTERVAL
;
2275 if (S_TM(n
->suffix
))
2276 strcpy(s
, str_initcap_z(localized_full_days
[tm
->tm_wday
]));
2278 sprintf(s
, "%*s", S_FM(n
->suffix
) ? 0 : -9, days
[tm
->tm_wday
]);
2282 INVALID_FOR_INTERVAL
;
2283 if (S_TM(n
->suffix
))
2284 strcpy(s
, str_tolower_z(localized_full_days
[tm
->tm_wday
]));
2287 sprintf(s
, "%*s", S_FM(n
->suffix
) ? 0 : -9, days
[tm
->tm_wday
]);
2288 *s
= pg_tolower((unsigned char) *s
);
2293 INVALID_FOR_INTERVAL
;
2294 if (S_TM(n
->suffix
))
2295 strcpy(s
, str_toupper_z(localized_abbrev_days
[tm
->tm_wday
]));
2297 strcpy(s
, str_toupper_z(days_short
[tm
->tm_wday
]));
2301 INVALID_FOR_INTERVAL
;
2302 if (S_TM(n
->suffix
))
2303 strcpy(s
, str_initcap_z(localized_abbrev_days
[tm
->tm_wday
]));
2305 strcpy(s
, days_short
[tm
->tm_wday
]);
2309 INVALID_FOR_INTERVAL
;
2310 if (S_TM(n
->suffix
))
2311 strcpy(s
, str_tolower_z(localized_abbrev_days
[tm
->tm_wday
]));
2314 strcpy(s
, days_short
[tm
->tm_wday
]);
2315 *s
= pg_tolower((unsigned char) *s
);
2321 sprintf(s
, "%0*d", S_FM(n
->suffix
) ? 0 : 3,
2322 (n
->key
->id
== DCH_DDD
) ?
2324 date2isoyearday(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
));
2325 if (S_THth(n
->suffix
))
2326 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2330 sprintf(s
, "%0*d", S_FM(n
->suffix
) ? 0 : 2, tm
->tm_mday
);
2331 if (S_THth(n
->suffix
))
2332 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2336 INVALID_FOR_INTERVAL
;
2337 sprintf(s
, "%d", tm
->tm_wday
+ 1);
2338 if (S_THth(n
->suffix
))
2339 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2343 INVALID_FOR_INTERVAL
;
2344 sprintf(s
, "%d", (tm
->tm_wday
== 0) ? 7 : tm
->tm_wday
);
2345 if (S_THth(n
->suffix
))
2346 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2350 sprintf(s
, "%0*d", S_FM(n
->suffix
) ? 0 : 2,
2351 (tm
->tm_yday
- 1) / 7 + 1);
2352 if (S_THth(n
->suffix
))
2353 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2357 sprintf(s
, "%0*d", S_FM(n
->suffix
) ? 0 : 2,
2358 date2isoweek(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
));
2359 if (S_THth(n
->suffix
))
2360 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2366 sprintf(s
, "%d", (tm
->tm_mon
- 1) / 3 + 1);
2367 if (S_THth(n
->suffix
))
2368 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2372 if (is_interval
) /* straight calculation */
2373 i
= tm
->tm_year
/ 100;
2374 else /* century 21 starts in 2001 */
2375 i
= (tm
->tm_year
- 1) / 100 + 1;
2376 if (i
<= 99 && i
>= -99)
2377 sprintf(s
, "%0*d", S_FM(n
->suffix
) ? 0 : 2, i
);
2379 sprintf(s
, "%d", i
);
2380 if (S_THth(n
->suffix
))
2381 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2385 i
= ADJUST_YEAR(tm
->tm_year
, is_interval
) / 1000;
2386 sprintf(s
, "%d,%03d", i
,
2387 ADJUST_YEAR(tm
->tm_year
, is_interval
) - (i
* 1000));
2388 if (S_THth(n
->suffix
))
2389 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2394 if (tm
->tm_year
<= 9999 && tm
->tm_year
>= -9998)
2396 S_FM(n
->suffix
) ? 0 : 4,
2397 n
->key
->id
== DCH_YYYY
?
2398 ADJUST_YEAR(tm
->tm_year
, is_interval
) :
2399 ADJUST_YEAR(date2isoyear(
2402 tm
->tm_mday
), is_interval
));
2405 n
->key
->id
== DCH_YYYY
?
2406 ADJUST_YEAR(tm
->tm_year
, is_interval
) :
2407 ADJUST_YEAR(date2isoyear(
2410 tm
->tm_mday
), is_interval
));
2411 if (S_THth(n
->suffix
))
2412 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2417 snprintf(buff
, sizeof(buff
), "%03d",
2418 n
->key
->id
== DCH_YYY
?
2419 ADJUST_YEAR(tm
->tm_year
, is_interval
) :
2420 ADJUST_YEAR(date2isoyear(tm
->tm_year
,
2421 tm
->tm_mon
, tm
->tm_mday
),
2424 strcpy(s
, buff
+ (i
- 3));
2425 if (S_THth(n
->suffix
))
2426 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2431 snprintf(buff
, sizeof(buff
), "%02d",
2432 n
->key
->id
== DCH_YY
?
2433 ADJUST_YEAR(tm
->tm_year
, is_interval
) :
2434 ADJUST_YEAR(date2isoyear(tm
->tm_year
,
2435 tm
->tm_mon
, tm
->tm_mday
),
2438 strcpy(s
, buff
+ (i
- 2));
2439 if (S_THth(n
->suffix
))
2440 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2445 snprintf(buff
, sizeof(buff
), "%1d",
2446 n
->key
->id
== DCH_Y
?
2447 ADJUST_YEAR(tm
->tm_year
, is_interval
) :
2448 ADJUST_YEAR(date2isoyear(tm
->tm_year
,
2449 tm
->tm_mon
, tm
->tm_mday
),
2452 strcpy(s
, buff
+ (i
- 1));
2453 if (S_THth(n
->suffix
))
2454 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2460 sprintf(s
, "%*s", S_FM(n
->suffix
) ? 0 : -4,
2461 rm_months_upper
[12 - tm
->tm_mon
]);
2467 sprintf(s
, "%*s", S_FM(n
->suffix
) ? 0 : -4,
2468 rm_months_lower
[12 - tm
->tm_mon
]);
2472 sprintf(s
, "%d", (tm
->tm_mday
- 1) / 7 + 1);
2473 if (S_THth(n
->suffix
))
2474 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2478 sprintf(s
, "%d", date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
));
2479 if (S_THth(n
->suffix
))
2480 str_numth(s
, s
, S_TH_TYPE(n
->suffix
));
2490 * Process a string as denoted by a list of FormatNodes.
2491 * The TmFromChar struct pointed to by 'out' is populated with the results.
2493 * Note: we currently don't have any to_interval() function, so there
2494 * is no need here for INVALID_FOR_INTERVAL checks.
2498 DCH_from_char(FormatNode
*node
, char *in
, TmFromChar
*out
)
2504 bool fx_mode
= false;
2506 for (n
= node
, s
= in
; n
->type
!= NODE_TYPE_END
&& *s
!= '\0'; n
++)
2508 if (n
->type
!= NODE_TYPE_ACTION
)
2511 /* Ignore spaces when not in FX (fixed width) mode */
2512 if (isspace((unsigned char) n
->character
) && !fx_mode
)
2514 while (*s
!= '\0' && isspace((unsigned char) *s
))
2520 from_char_set_mode(out
, n
->key
->date_mode
);
2531 from_char_seq_search(&value
, &s
, ampm_strings_long
,
2532 ALL_UPPER
, n
->key
->len
, n
);
2533 from_char_set_int(&out
->pm
, value
% 2, n
);
2534 out
->clock
= CLOCK_12_HOUR
;
2540 from_char_seq_search(&value
, &s
, ampm_strings
,
2541 ALL_UPPER
, n
->key
->len
, n
);
2542 from_char_set_int(&out
->pm
, value
% 2, n
);
2543 out
->clock
= CLOCK_12_HOUR
;
2547 from_char_parse_int_len(&out
->hh
, &s
, 2, n
);
2548 out
->clock
= CLOCK_12_HOUR
;
2549 s
+= SKIP_THth(n
->suffix
);
2552 from_char_parse_int_len(&out
->hh
, &s
, 2, n
);
2553 s
+= SKIP_THth(n
->suffix
);
2556 from_char_parse_int(&out
->mi
, &s
, n
);
2557 s
+= SKIP_THth(n
->suffix
);
2560 from_char_parse_int(&out
->ss
, &s
, n
);
2561 s
+= SKIP_THth(n
->suffix
);
2563 case DCH_MS
: /* millisecond */
2564 len
= from_char_parse_int_len(&out
->ms
, &s
, 3, n
);
2567 * 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25
2569 out
->ms
*= len
== 1 ? 100 :
2572 s
+= SKIP_THth(n
->suffix
);
2574 case DCH_US
: /* microsecond */
2575 len
= from_char_parse_int_len(&out
->us
, &s
, 6, n
);
2577 out
->us
*= len
== 1 ? 100000 :
2583 s
+= SKIP_THth(n
->suffix
);
2586 from_char_parse_int(&out
->ssss
, &s
, n
);
2587 s
+= SKIP_THth(n
->suffix
);
2592 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2593 errmsg("\"TZ\"/\"tz\" format patterns are not supported in to_date")));
2598 from_char_seq_search(&value
, &s
, adbc_strings_long
,
2599 ALL_UPPER
, n
->key
->len
, n
);
2600 from_char_set_int(&out
->bc
, value
% 2, n
);
2606 from_char_seq_search(&value
, &s
, adbc_strings
,
2607 ALL_UPPER
, n
->key
->len
, n
);
2608 from_char_set_int(&out
->bc
, value
% 2, n
);
2613 from_char_seq_search(&value
, &s
, months_full
, ONE_UPPER
,
2615 from_char_set_int(&out
->mm
, value
+ 1, n
);
2620 from_char_seq_search(&value
, &s
, months
, ONE_UPPER
,
2622 from_char_set_int(&out
->mm
, value
+ 1, n
);
2625 from_char_parse_int(&out
->mm
, &s
, n
);
2626 s
+= SKIP_THth(n
->suffix
);
2631 from_char_seq_search(&value
, &s
, days
, ONE_UPPER
,
2633 from_char_set_int(&out
->d
, value
, n
);
2638 from_char_seq_search(&value
, &s
, days
, ONE_UPPER
,
2640 from_char_set_int(&out
->d
, value
, n
);
2643 from_char_parse_int(&out
->ddd
, &s
, n
);
2644 s
+= SKIP_THth(n
->suffix
);
2647 from_char_parse_int_len(&out
->ddd
, &s
, 3, n
);
2648 s
+= SKIP_THth(n
->suffix
);
2651 from_char_parse_int(&out
->dd
, &s
, n
);
2652 s
+= SKIP_THth(n
->suffix
);
2655 from_char_parse_int(&out
->d
, &s
, n
);
2657 s
+= SKIP_THth(n
->suffix
);
2660 from_char_parse_int_len(&out
->d
, &s
, 1, n
);
2661 s
+= SKIP_THth(n
->suffix
);
2665 from_char_parse_int(&out
->ww
, &s
, n
);
2666 s
+= SKIP_THth(n
->suffix
);
2671 * We ignore Q when converting to date because it is not
2674 * We still parse the source string for an integer, but it
2675 * isn't stored anywhere in 'out'.
2677 from_char_parse_int((int *) NULL
, &s
, n
);
2678 s
+= SKIP_THth(n
->suffix
);
2681 from_char_parse_int(&out
->cc
, &s
, n
);
2682 s
+= SKIP_THth(n
->suffix
);
2690 matched
= sscanf(s
, "%d,%03d", &millenia
, &years
);
2693 (errcode(ERRCODE_INVALID_DATETIME_FORMAT
),
2694 errmsg("invalid input string for \"Y,YYY\"")));
2695 years
+= (millenia
* 1000);
2696 from_char_set_int(&out
->year
, years
, n
);
2698 s
+= strdigits_len(s
) + 4 + SKIP_THth(n
->suffix
);
2703 from_char_parse_int(&out
->year
, &s
, n
);
2705 s
+= SKIP_THth(n
->suffix
);
2709 from_char_parse_int(&out
->year
, &s
, n
);
2713 * 3-digit year: '100' ... '999' = 1100 ... 1999 '000' ...
2714 * '099' = 2000 ... 2099
2716 if (out
->year
>= 100)
2720 s
+= SKIP_THth(n
->suffix
);
2724 from_char_parse_int(&out
->year
, &s
, n
);
2728 * 2-digit year: '00' ... '69' = 2000 ... 2069 '70' ... '99'
2735 s
+= SKIP_THth(n
->suffix
);
2739 from_char_parse_int(&out
->year
, &s
, n
);
2743 * 1-digit year: always +2000
2746 s
+= SKIP_THth(n
->suffix
);
2749 from_char_seq_search(&value
, &s
, rm_months_upper
,
2750 ALL_UPPER
, MAX_RM_LEN
, n
);
2751 from_char_set_int(&out
->mm
, 12 - value
, n
);
2754 from_char_seq_search(&value
, &s
, rm_months_lower
,
2755 ALL_LOWER
, MAX_RM_LEN
, n
);
2756 from_char_set_int(&out
->mm
, 12 - value
, n
);
2759 from_char_parse_int(&out
->w
, &s
, n
);
2760 s
+= SKIP_THth(n
->suffix
);
2763 from_char_parse_int(&out
->j
, &s
, n
);
2764 s
+= SKIP_THth(n
->suffix
);
2770 static DCHCacheEntry
*
2771 DCH_cache_getnew(char *str
)
2775 /* counter overflow check - paranoia? */
2776 if (DCHCounter
>= (INT_MAX
- DCH_CACHE_FIELDS
- 1))
2780 for (ent
= DCHCache
; ent
<= (DCHCache
+ DCH_CACHE_FIELDS
); ent
++)
2781 ent
->age
= (++DCHCounter
);
2785 * If cache is full, remove oldest entry
2787 if (n_DCHCache
> DCH_CACHE_FIELDS
)
2789 DCHCacheEntry
*old
= DCHCache
+ 0;
2791 #ifdef DEBUG_TO_FROM_CHAR
2792 elog(DEBUG_elog_output
, "cache is full (%d)", n_DCHCache
);
2794 for (ent
= DCHCache
+ 1; ent
<= (DCHCache
+ DCH_CACHE_FIELDS
); ent
++)
2796 if (ent
->age
< old
->age
)
2799 #ifdef DEBUG_TO_FROM_CHAR
2800 elog(DEBUG_elog_output
, "OLD: '%s' AGE: %d", old
->str
, old
->age
);
2802 StrNCpy(old
->str
, str
, DCH_CACHE_SIZE
+ 1);
2803 /* old->format fill parser */
2804 old
->age
= (++DCHCounter
);
2809 #ifdef DEBUG_TO_FROM_CHAR
2810 elog(DEBUG_elog_output
, "NEW (%d)", n_DCHCache
);
2812 ent
= DCHCache
+ n_DCHCache
;
2813 StrNCpy(ent
->str
, str
, DCH_CACHE_SIZE
+ 1);
2814 /* ent->format fill parser */
2815 ent
->age
= (++DCHCounter
);
2821 static DCHCacheEntry
*
2822 DCH_cache_search(char *str
)
2827 /* counter overflow check - paranoia? */
2828 if (DCHCounter
>= (INT_MAX
- DCH_CACHE_FIELDS
- 1))
2832 for (ent
= DCHCache
; ent
<= (DCHCache
+ DCH_CACHE_FIELDS
); ent
++)
2833 ent
->age
= (++DCHCounter
);
2836 for (i
= 0, ent
= DCHCache
; i
< n_DCHCache
; i
++, ent
++)
2838 if (strcmp(ent
->str
, str
) == 0)
2840 ent
->age
= (++DCHCounter
);
2849 * Format a date/time or interval into a string according to fmt.
2850 * We parse fmt into a list of FormatNodes. This is then passed to DCH_to_char
2854 datetime_to_char_body(TmToChar
*tmtc
, text
*fmt
, bool is_interval
)
2864 * Convert fmt to C string
2866 fmt_str
= text_to_cstring(fmt
);
2867 fmt_len
= strlen(fmt_str
);
2870 * Allocate workspace for result as C string
2872 result
= palloc((fmt_len
* DCH_MAX_ITEM_SIZ
) + 1);
2876 * Allocate new memory if format picture is bigger than static cache and
2877 * not use cache (call parser always)
2879 if (fmt_len
> DCH_CACHE_SIZE
)
2881 format
= (FormatNode
*) palloc((fmt_len
+ 1) * sizeof(FormatNode
));
2884 parse_format(format
, fmt_str
, DCH_keywords
,
2885 DCH_suff
, DCH_index
, DCH_TYPE
, NULL
);
2887 (format
+ fmt_len
)->type
= NODE_TYPE_END
; /* Paranoia? */
2898 if ((ent
= DCH_cache_search(fmt_str
)) == NULL
)
2900 ent
= DCH_cache_getnew(fmt_str
);
2903 * Not in the cache, must run parser and save a new format-picture
2906 parse_format(ent
->format
, fmt_str
, DCH_keywords
,
2907 DCH_suff
, DCH_index
, DCH_TYPE
, NULL
);
2909 (ent
->format
+ fmt_len
)->type
= NODE_TYPE_END
; /* Paranoia? */
2911 #ifdef DEBUG_TO_FROM_CHAR
2912 /* dump_node(ent->format, fmt_len); */
2913 /* dump_index(DCH_keywords, DCH_index); */
2916 format
= ent
->format
;
2919 /* The real work is here */
2920 DCH_to_char(format
, is_interval
, tmtc
, result
);
2927 /* convert C-string result to TEXT format */
2928 res
= cstring_to_text(result
);
2934 /****************************************************************************
2936 ***************************************************************************/
2938 /* -------------------
2939 * TIMESTAMP to_char()
2940 * -------------------
2943 timestamp_to_char(PG_FUNCTION_ARGS
)
2945 Timestamp dt
= PG_GETARG_TIMESTAMP(0);
2946 text
*fmt
= PG_GETARG_TEXT_P(1),
2952 if ((VARSIZE(fmt
) - VARHDRSZ
) <= 0 || TIMESTAMP_NOT_FINITE(dt
))
2958 if (timestamp2tm(dt
, NULL
, tm
, &tmtcFsec(&tmtc
), NULL
, NULL
) != 0)
2960 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2961 errmsg("timestamp out of range")));
2963 thisdate
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
2964 tm
->tm_wday
= (thisdate
+ 1) % 7;
2965 tm
->tm_yday
= thisdate
- date2j(tm
->tm_year
, 1, 1) + 1;
2967 if (!(res
= datetime_to_char_body(&tmtc
, fmt
, false)))
2970 PG_RETURN_TEXT_P(res
);
2974 timestamptz_to_char(PG_FUNCTION_ARGS
)
2976 TimestampTz dt
= PG_GETARG_TIMESTAMP(0);
2977 text
*fmt
= PG_GETARG_TEXT_P(1),
2984 if ((VARSIZE(fmt
) - VARHDRSZ
) <= 0 || TIMESTAMP_NOT_FINITE(dt
))
2990 if (timestamp2tm(dt
, &tz
, tm
, &tmtcFsec(&tmtc
), &tmtcTzn(&tmtc
), NULL
) != 0)
2992 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2993 errmsg("timestamp out of range")));
2995 thisdate
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
2996 tm
->tm_wday
= (thisdate
+ 1) % 7;
2997 tm
->tm_yday
= thisdate
- date2j(tm
->tm_year
, 1, 1) + 1;
2999 if (!(res
= datetime_to_char_body(&tmtc
, fmt
, false)))
3002 PG_RETURN_TEXT_P(res
);
3006 /* -------------------
3007 * INTERVAL to_char()
3008 * -------------------
3011 interval_to_char(PG_FUNCTION_ARGS
)
3013 Interval
*it
= PG_GETARG_INTERVAL_P(0);
3014 text
*fmt
= PG_GETARG_TEXT_P(1),
3019 if ((VARSIZE(fmt
) - VARHDRSZ
) <= 0)
3025 if (interval2tm(*it
, tm
, &tmtcFsec(&tmtc
)) != 0)
3028 /* wday is meaningless, yday approximates the total span in days */
3029 tm
->tm_yday
= (tm
->tm_year
* MONTHS_PER_YEAR
+ tm
->tm_mon
) * DAYS_PER_MONTH
+ tm
->tm_mday
;
3031 if (!(res
= datetime_to_char_body(&tmtc
, fmt
, true)))
3034 PG_RETURN_TEXT_P(res
);
3037 /* ---------------------
3040 * Make Timestamp from date_str which is formatted at argument 'fmt'
3041 * ( to_timestamp is reverse to_char() )
3042 * ---------------------
3045 to_timestamp(PG_FUNCTION_ARGS
)
3047 text
*date_txt
= PG_GETARG_TEXT_P(0);
3048 text
*fmt
= PG_GETARG_TEXT_P(1);
3054 do_to_timestamp(date_txt
, fmt
, &tm
, &fsec
);
3056 tz
= DetermineTimeZoneOffset(&tm
, session_timezone
);
3058 if (tm2timestamp(&tm
, fsec
, &tz
, &result
) != 0)
3060 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3061 errmsg("timestamp out of range")));
3063 PG_RETURN_TIMESTAMP(result
);
3068 * Make Date from date_str which is formated at argument 'fmt'
3072 to_date(PG_FUNCTION_ARGS
)
3074 text
*date_txt
= PG_GETARG_TEXT_P(0);
3075 text
*fmt
= PG_GETARG_TEXT_P(1);
3080 do_to_timestamp(date_txt
, fmt
, &tm
, &fsec
);
3082 result
= date2j(tm
.tm_year
, tm
.tm_mon
, tm
.tm_mday
) - POSTGRES_EPOCH_JDATE
;
3084 PG_RETURN_DATEADT(result
);
3088 * do_to_timestamp: shared code for to_timestamp and to_date
3090 * Parse the 'date_txt' according to 'fmt', return results as a struct pg_tm
3091 * and fractional seconds.
3093 * We parse 'fmt' into a list of FormatNodes, which is then passed to
3094 * DCH_from_char to populate a TmFromChar with the parsed contents of
3097 * The TmFromChar is then analysed and converted into the final results in
3098 * struct 'tm' and 'fsec'.
3101 do_to_timestamp(text
*date_txt
, text
*fmt
,
3102 struct pg_tm
* tm
, fsec_t
*fsec
)
3112 fmt_len
= VARSIZE_ANY_EXHDR(fmt
);
3120 fmt_str
= text_to_cstring(fmt
);
3123 * Allocate new memory if format picture is bigger than static cache
3124 * and not use cache (call parser always)
3126 if (fmt_len
> DCH_CACHE_SIZE
)
3128 format
= (FormatNode
*) palloc((fmt_len
+ 1) * sizeof(FormatNode
));
3131 parse_format(format
, fmt_str
, DCH_keywords
,
3132 DCH_suff
, DCH_index
, DCH_TYPE
, NULL
);
3134 (format
+ fmt_len
)->type
= NODE_TYPE_END
; /* Paranoia? */
3145 if ((ent
= DCH_cache_search(fmt_str
)) == NULL
)
3147 ent
= DCH_cache_getnew(fmt_str
);
3150 * Not in the cache, must run parser and save a new
3151 * format-picture to the cache.
3153 parse_format(ent
->format
, fmt_str
, DCH_keywords
,
3154 DCH_suff
, DCH_index
, DCH_TYPE
, NULL
);
3156 (ent
->format
+ fmt_len
)->type
= NODE_TYPE_END
; /* Paranoia? */
3157 #ifdef DEBUG_TO_FROM_CHAR
3158 /* dump_node(ent->format, fmt_len); */
3159 /* dump_index(DCH_keywords, DCH_index); */
3162 format
= ent
->format
;
3165 #ifdef DEBUG_TO_FROM_CHAR
3166 /* dump_node(format, fmt_len); */
3169 date_str
= text_to_cstring(date_txt
);
3171 DCH_from_char(format
, date_str
, &tmfc
);
3182 * Convert values that user define for FROM_CHAR (to_date/to_timestamp) to
3189 tm
->tm_hour
= x
/ SECS_PER_HOUR
;
3191 tm
->tm_min
= x
/ SECS_PER_MINUTE
;
3192 x
%= SECS_PER_MINUTE
;
3197 tm
->tm_sec
= tmfc
.ss
;
3199 tm
->tm_min
= tmfc
.mi
;
3201 tm
->tm_hour
= tmfc
.hh
;
3203 if (tmfc
.clock
== CLOCK_12_HOUR
)
3205 if (tm
->tm_hour
< 1 || tm
->tm_hour
> 12)
3207 (errcode(ERRCODE_INVALID_DATETIME_FORMAT
),
3208 errmsg("hour \"%d\" is invalid for the 12-hour clock",
3210 errhint("Use the 24-hour clock, or give an hour between 1 and 12.")));
3212 if (tmfc
.pm
&& tm
->tm_hour
< 12)
3214 else if (!tmfc
.pm
&& tm
->tm_hour
== 12)
3221 * If CC and YY (or Y) are provided, use YY as 2 low-order digits for
3222 * the year in the given century. Keep in mind that the 21st century
3223 * runs from 2001-2100, not 2000-2099.
3225 * If a 4-digit year is provided, we use that and ignore CC.
3227 if (tmfc
.cc
&& tmfc
.yysz
<= 2)
3229 tm
->tm_year
= tmfc
.year
% 100;
3231 tm
->tm_year
+= (tmfc
.cc
- 1) * 100;
3233 tm
->tm_year
= tmfc
.cc
* 100;
3236 tm
->tm_year
= tmfc
.year
;
3238 else if (tmfc
.cc
) /* use first year of century */
3239 tm
->tm_year
= (tmfc
.cc
- 1) * 100 + 1;
3243 if (tm
->tm_year
> 0)
3244 tm
->tm_year
= -(tm
->tm_year
- 1);
3247 (errcode(ERRCODE_INVALID_DATETIME_FORMAT
),
3248 errmsg("inconsistent use of year %04d and \"BC\"",
3253 j2date(tmfc
.j
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
3257 if (tmfc
.mode
== FROM_CHAR_DATE_ISOWEEK
)
3260 * If tmfc.d is not set, then the date is left at the beginning of
3261 * the ISO week (Monday).
3264 isoweekdate2date(tmfc
.ww
, tmfc
.d
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
3266 isoweek2date(tmfc
.ww
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
3269 tmfc
.ddd
= (tmfc
.ww
- 1) * 7 + 1;
3273 tmfc
.dd
= (tmfc
.w
- 1) * 7 + 1;
3275 tm
->tm_wday
= tmfc
.d
;
3277 tm
->tm_mday
= tmfc
.dd
;
3279 tm
->tm_yday
= tmfc
.ddd
;
3281 tm
->tm_mon
= tmfc
.mm
;
3283 if (tmfc
.ddd
&& (tm
->tm_mon
<= 1 || tm
->tm_mday
<= 1))
3286 * The month and day field have not been set, so we use the
3287 * day-of-year field to populate them. Depending on the date mode,
3288 * this field may be interpreted as a Gregorian day-of-year, or an ISO
3289 * week date day-of-year.
3292 if (!tm
->tm_year
&& !tmfc
.bc
)
3294 (errcode(ERRCODE_INVALID_DATETIME_FORMAT
),
3295 errmsg("cannot calculate day of year without year information")));
3297 if (tmfc
.mode
== FROM_CHAR_DATE_ISOWEEK
)
3299 int j0
; /* zeroth day of the ISO year, in Julian */
3301 j0
= isoweek2j(tm
->tm_year
, 1) - 1;
3303 j2date(j0
+ tmfc
.ddd
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
3310 static const int ysum
[2][13] = {
3311 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
3312 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};
3314 y
= ysum
[isleap(tm
->tm_year
)];
3316 for (i
= 1; i
<= 12; i
++)
3318 if (tmfc
.ddd
< y
[i
])
3321 if (tm
->tm_mon
<= 1)
3324 if (tm
->tm_mday
<= 1)
3325 tm
->tm_mday
= tmfc
.ddd
- y
[i
- 1];
3329 #ifdef HAVE_INT64_TIMESTAMP
3331 *fsec
+= tmfc
.ms
* 1000;
3336 *fsec
+= (double) tmfc
.ms
/ 1000;
3338 *fsec
+= (double) tmfc
.us
/ 1000000;
3345 /**********************************************************************
3346 * the NUMBER version part
3347 *********************************************************************/
3351 fill_str(char *str
, int c
, int max
)
3353 memset(str
, c
, max
);
3354 *(str
+ max
) = '\0';
3358 #define zeroize_NUM(_n) \
3364 (_n)->pre_lsign_num = 0; \
3365 (_n)->need_locale = 0; \
3367 (_n)->zero_start = 0; \
3368 (_n)->zero_end = 0; \
3371 static NUMCacheEntry
*
3372 NUM_cache_getnew(char *str
)
3376 /* counter overflow check - paranoia? */
3377 if (NUMCounter
>= (INT_MAX
- NUM_CACHE_FIELDS
- 1))
3381 for (ent
= NUMCache
; ent
<= (NUMCache
+ NUM_CACHE_FIELDS
); ent
++)
3382 ent
->age
= (++NUMCounter
);
3386 * If cache is full, remove oldest entry
3388 if (n_NUMCache
> NUM_CACHE_FIELDS
)
3390 NUMCacheEntry
*old
= NUMCache
+ 0;
3392 #ifdef DEBUG_TO_FROM_CHAR
3393 elog(DEBUG_elog_output
, "Cache is full (%d)", n_NUMCache
);
3395 for (ent
= NUMCache
; ent
<= (NUMCache
+ NUM_CACHE_FIELDS
); ent
++)
3398 * entry removed via NUM_cache_remove() can be used here, which is
3399 * why it's worth scanning first entry again
3401 if (ent
->str
[0] == '\0')
3406 if (ent
->age
< old
->age
)
3409 #ifdef DEBUG_TO_FROM_CHAR
3410 elog(DEBUG_elog_output
, "OLD: \"%s\" AGE: %d", old
->str
, old
->age
);
3412 StrNCpy(old
->str
, str
, NUM_CACHE_SIZE
+ 1);
3413 /* old->format fill parser */
3414 old
->age
= (++NUMCounter
);
3419 #ifdef DEBUG_TO_FROM_CHAR
3420 elog(DEBUG_elog_output
, "NEW (%d)", n_NUMCache
);
3422 ent
= NUMCache
+ n_NUMCache
;
3423 StrNCpy(ent
->str
, str
, NUM_CACHE_SIZE
+ 1);
3424 /* ent->format fill parser */
3425 ent
->age
= (++NUMCounter
);
3429 zeroize_NUM(&ent
->Num
);
3431 last_NUMCacheEntry
= ent
;
3435 static NUMCacheEntry
*
3436 NUM_cache_search(char *str
)
3441 /* counter overflow check - paranoia? */
3442 if (NUMCounter
>= (INT_MAX
- NUM_CACHE_FIELDS
- 1))
3446 for (ent
= NUMCache
; ent
<= (NUMCache
+ NUM_CACHE_FIELDS
); ent
++)
3447 ent
->age
= (++NUMCounter
);
3450 for (i
= 0, ent
= NUMCache
; i
< n_NUMCache
; i
++, ent
++)
3452 if (strcmp(ent
->str
, str
) == 0)
3454 ent
->age
= (++NUMCounter
);
3455 last_NUMCacheEntry
= ent
;
3464 NUM_cache_remove(NUMCacheEntry
*ent
)
3466 #ifdef DEBUG_TO_FROM_CHAR
3467 elog(DEBUG_elog_output
, "REMOVING ENTRY (%s)", ent
->str
);
3474 * Cache routine for NUM to_char version
3478 NUM_cache(int len
, NUMDesc
*Num
, text
*pars_str
, bool *shouldFree
)
3480 FormatNode
*format
= NULL
;
3483 str
= text_to_cstring(pars_str
);
3486 * Allocate new memory if format picture is bigger than static cache and
3487 * not use cache (call parser always). This branches sets shouldFree to
3488 * true, accordingly.
3490 if (len
> NUM_CACHE_SIZE
)
3492 format
= (FormatNode
*) palloc((len
+ 1) * sizeof(FormatNode
));
3498 parse_format(format
, str
, NUM_keywords
,
3499 NULL
, NUM_index
, NUM_TYPE
, Num
);
3501 (format
+ len
)->type
= NODE_TYPE_END
; /* Paranoia? */
3510 *shouldFree
= false;
3512 if ((ent
= NUM_cache_search(str
)) == NULL
)
3514 ent
= NUM_cache_getnew(str
);
3517 * Not in the cache, must run parser and save a new format-picture
3520 parse_format(ent
->format
, str
, NUM_keywords
,
3521 NULL
, NUM_index
, NUM_TYPE
, &ent
->Num
);
3523 (ent
->format
+ len
)->type
= NODE_TYPE_END
; /* Paranoia? */
3526 format
= ent
->format
;
3529 * Copy cache to used struct
3531 Num
->flag
= ent
->Num
.flag
;
3532 Num
->lsign
= ent
->Num
.lsign
;
3533 Num
->pre
= ent
->Num
.pre
;
3534 Num
->post
= ent
->Num
.post
;
3535 Num
->pre_lsign_num
= ent
->Num
.pre_lsign_num
;
3536 Num
->need_locale
= ent
->Num
.need_locale
;
3537 Num
->multi
= ent
->Num
.multi
;
3538 Num
->zero_start
= ent
->Num
.zero_start
;
3539 Num
->zero_end
= ent
->Num
.zero_end
;
3542 #ifdef DEBUG_TO_FROM_CHAR
3543 /* dump_node(format, len); */
3544 dump_index(NUM_keywords
, NUM_index
);
3553 int_to_roman(int number
)
3561 result
= (char *) palloc(16);
3564 if (number
> 3999 || number
< 1)
3566 fill_str(result
, '#', 15);
3569 len
= snprintf(numstr
, sizeof(numstr
), "%d", number
);
3571 for (p
= numstr
; *p
!= '\0'; p
++, --len
)
3573 num
= *p
- 49; /* 48 ascii + 1 */
3580 strcat(result
, "M");
3585 strcat(result
, rm100
[num
]);
3587 strcat(result
, rm10
[num
]);
3589 strcat(result
, rm1
[num
]);
3602 NUM_prepare_locale(NUMProc
*Np
)
3604 if (Np
->Num
->need_locale
)
3606 struct lconv
*lconv
;
3611 lconv
= PGLC_localeconv();
3614 * Positive / Negative number sign
3616 if (lconv
->negative_sign
&& *lconv
->negative_sign
)
3617 Np
->L_negative_sign
= lconv
->negative_sign
;
3619 Np
->L_negative_sign
= "-";
3621 if (lconv
->positive_sign
&& *lconv
->positive_sign
)
3622 Np
->L_positive_sign
= lconv
->positive_sign
;
3624 Np
->L_positive_sign
= "+";
3627 * Number decimal point
3629 if (lconv
->decimal_point
&& *lconv
->decimal_point
)
3630 Np
->decimal
= lconv
->decimal_point
;
3635 if (!IS_LDECIMAL(Np
->Num
))
3639 * Number thousands separator
3641 * Some locales (e.g. broken glibc pt_BR), have a comma for decimal,
3642 * but "" for thousands_sep, so we set the thousands_sep too.
3643 * http://archives.postgresql.org/pgsql-hackers/2007-11/msg00772.php
3645 if (lconv
->thousands_sep
&& *lconv
->thousands_sep
)
3646 Np
->L_thousands_sep
= lconv
->thousands_sep
;
3647 /* Make sure thousands separator doesn't match decimal point symbol. */
3648 else if (strcmp(Np
->decimal
, ",") !=0)
3649 Np
->L_thousands_sep
= ",";
3651 Np
->L_thousands_sep
= ".";
3656 if (lconv
->currency_symbol
&& *lconv
->currency_symbol
)
3657 Np
->L_currency_symbol
= lconv
->currency_symbol
;
3659 Np
->L_currency_symbol
= " ";
3666 Np
->L_negative_sign
= "-";
3667 Np
->L_positive_sign
= "+";
3670 Np
->L_thousands_sep
= ",";
3671 Np
->L_currency_symbol
= " ";
3676 * Return pointer of last relevant number after decimal point
3677 * 12.0500 --> last relevant is '5'
3681 get_last_relevant_decnum(char *num
)
3684 *p
= strchr(num
, '.');
3686 #ifdef DEBUG_TO_FROM_CHAR
3687 elog(DEBUG_elog_output
, "get_last_relevant_decnum()");
3704 * Number extraction for TO_NUMBER()
3708 NUM_numpart_from_char(NUMProc
*Np
, int id
, int plen
)
3710 bool isread
= FALSE
;
3712 #ifdef DEBUG_TO_FROM_CHAR
3713 elog(DEBUG_elog_output
, " --- scan start --- id=%s",
3714 (id
== NUM_0
|| id
== NUM_9
) ? "NUM_0/9" : id
== NUM_DEC
? "NUM_DEC" : "???");
3717 if (*Np
->inout_p
== ' ')
3720 #define OVERLOAD_TEST (Np->inout_p >= Np->inout + plen)
3721 #define AMOUNT_TEST(_s) (plen-(Np->inout_p-Np->inout) >= _s)
3723 if (*Np
->inout_p
== ' ')
3730 * read sign before number
3732 if (*Np
->number
== ' ' && (id
== NUM_0
|| id
== NUM_9
) &&
3733 (Np
->read_pre
+ Np
->read_post
) == 0)
3735 #ifdef DEBUG_TO_FROM_CHAR
3736 elog(DEBUG_elog_output
, "Try read sign (%c), locale positive: %s, negative: %s",
3737 *Np
->inout_p
, Np
->L_positive_sign
, Np
->L_negative_sign
);
3743 if (IS_LSIGN(Np
->Num
) && Np
->Num
->lsign
== NUM_LSIGN_PRE
)
3747 #ifdef DEBUG_TO_FROM_CHAR
3748 elog(DEBUG_elog_output
, "Try read locale pre-sign (%c)", *Np
->inout_p
);
3750 if ((x
= strlen(Np
->L_negative_sign
)) &&
3752 strncmp(Np
->inout_p
, Np
->L_negative_sign
, x
) == 0)
3757 else if ((x
= strlen(Np
->L_positive_sign
)) &&
3759 strncmp(Np
->inout_p
, Np
->L_positive_sign
, x
) == 0)
3767 #ifdef DEBUG_TO_FROM_CHAR
3768 elog(DEBUG_elog_output
, "Try read simple sign (%c)", *Np
->inout_p
);
3774 if (*Np
->inout_p
== '-' || (IS_BRACKET(Np
->Num
) &&
3775 *Np
->inout_p
== '<'))
3777 *Np
->number
= '-'; /* set - */
3780 else if (*Np
->inout_p
== '+')
3782 *Np
->number
= '+'; /* set + */
3791 #ifdef DEBUG_TO_FROM_CHAR
3792 elog(DEBUG_elog_output
, "Scan for numbers (%c), current number: '%s'", *Np
->inout_p
, Np
->number
);
3798 if (isdigit((unsigned char) *Np
->inout_p
))
3800 if (Np
->read_dec
&& Np
->read_post
== Np
->Num
->post
)
3803 *Np
->number_p
= *Np
->inout_p
;
3813 #ifdef DEBUG_TO_FROM_CHAR
3814 elog(DEBUG_elog_output
, "Read digit (%c)", *Np
->inout_p
);
3818 * read decimal point
3821 else if (IS_DECIMAL(Np
->Num
) && Np
->read_dec
== FALSE
)
3823 #ifdef DEBUG_TO_FROM_CHAR
3824 elog(DEBUG_elog_output
, "Try read decimal point (%c)", *Np
->inout_p
);
3826 if (*Np
->inout_p
== '.')
3828 *Np
->number_p
= '.';
3830 Np
->read_dec
= TRUE
;
3835 int x
= strlen(Np
->decimal
);
3837 #ifdef DEBUG_TO_FROM_CHAR
3838 elog(DEBUG_elog_output
, "Try read locale point (%c)",
3841 if (x
&& AMOUNT_TEST(x
) && strncmp(Np
->inout_p
, Np
->decimal
, x
) == 0)
3843 Np
->inout_p
+= x
- 1;
3844 *Np
->number_p
= '.';
3846 Np
->read_dec
= TRUE
;
3856 * Read sign behind "last" number
3858 * We need sign detection because determine exact position of post-sign is
3861 * FM9999.9999999S -> 123.001- 9.9S -> .5- FM9.999999MI ->
3864 if (*Np
->number
== ' ' && Np
->read_pre
+ Np
->read_post
> 0)
3867 * locale sign (NUM_S) is always anchored behind a last number, if: -
3868 * locale sign expected - last read char was NUM_0/9 or NUM_DEC - and
3869 * next char is not digit
3871 if (IS_LSIGN(Np
->Num
) && isread
&&
3872 (Np
->inout_p
+ 1) <= Np
->inout
+ plen
&&
3873 !isdigit((unsigned char) *(Np
->inout_p
+ 1)))
3876 char *tmp
= Np
->inout_p
++;
3878 #ifdef DEBUG_TO_FROM_CHAR
3879 elog(DEBUG_elog_output
, "Try read locale post-sign (%c)", *Np
->inout_p
);
3881 if ((x
= strlen(Np
->L_negative_sign
)) &&
3883 strncmp(Np
->inout_p
, Np
->L_negative_sign
, x
) == 0)
3885 Np
->inout_p
+= x
- 1; /* -1 .. NUM_processor() do inout_p++ */
3888 else if ((x
= strlen(Np
->L_positive_sign
)) &&
3890 strncmp(Np
->inout_p
, Np
->L_positive_sign
, x
) == 0)
3892 Np
->inout_p
+= x
- 1; /* -1 .. NUM_processor() do inout_p++ */
3895 if (*Np
->number
== ' ')
3901 * try read non-locale sign, it's happen only if format is not exact
3902 * and we cannot determine sign position of MI/PL/SG, an example:
3904 * FM9.999999MI -> 5.01-
3906 * if (.... && IS_LSIGN(Np->Num)==FALSE) prevents read wrong formats
3907 * like to_number('1 -', '9S') where sign is not anchored to last
3910 else if (isread
== FALSE
&& IS_LSIGN(Np
->Num
) == FALSE
&&
3911 (IS_PLUS(Np
->Num
) || IS_MINUS(Np
->Num
)))
3913 #ifdef DEBUG_TO_FROM_CHAR
3914 elog(DEBUG_elog_output
, "Try read simple post-sign (%c)", *Np
->inout_p
);
3920 if (*Np
->inout_p
== '-' || *Np
->inout_p
== '+')
3921 /* NUM_processor() do inout_p++ */
3922 *Np
->number
= *Np
->inout_p
;
3927 #define IS_PREDEC_SPACE(_n) \
3928 (IS_ZERO((_n)->Num)==FALSE && \
3929 (_n)->number == (_n)->number_p && \
3930 *(_n)->number == '0' && \
3931 (_n)->Num->post != 0)
3934 * Add digit or sign to number-string
3938 NUM_numpart_to_char(NUMProc
*Np
, int id
)
3942 if (IS_ROMAN(Np
->Num
))
3945 /* Note: in this elog() output not set '\0' in 'inout' */
3947 #ifdef DEBUG_TO_FROM_CHAR
3950 * Np->num_curr is number of current item in format-picture, it is not
3951 * current position in inout!
3953 elog(DEBUG_elog_output
,
3954 "SIGN_WROTE: %d, CURRENT: %d, NUMBER_P: \"%s\", INOUT: \"%s\"",
3963 * Write sign if real number will write to output Note: IS_PREDEC_SPACE()
3964 * handle "9.9" --> " .1"
3966 if (Np
->sign_wrote
== FALSE
&&
3967 (Np
->num_curr
>= Np
->num_pre
|| (IS_ZERO(Np
->Num
) && Np
->Num
->zero_start
== Np
->num_curr
)) &&
3968 (IS_PREDEC_SPACE(Np
) == FALSE
|| (Np
->last_relevant
&& *Np
->last_relevant
== '.')))
3970 if (IS_LSIGN(Np
->Num
))
3972 if (Np
->Num
->lsign
== NUM_LSIGN_PRE
)
3974 if (Np
->sign
== '-')
3975 strcpy(Np
->inout_p
, Np
->L_negative_sign
);
3977 strcpy(Np
->inout_p
, Np
->L_positive_sign
);
3978 Np
->inout_p
+= strlen(Np
->inout_p
);
3979 Np
->sign_wrote
= TRUE
;
3982 else if (IS_BRACKET(Np
->Num
))
3984 *Np
->inout_p
= Np
->sign
== '+' ? ' ' : '<';
3986 Np
->sign_wrote
= TRUE
;
3988 else if (Np
->sign
== '+')
3990 if (!IS_FILLMODE(Np
->Num
))
3992 *Np
->inout_p
= ' '; /* Write + */
3995 Np
->sign_wrote
= TRUE
;
3997 else if (Np
->sign
== '-')
4001 Np
->sign_wrote
= TRUE
;
4007 * digits / FM / Zero / Dec. point
4009 if (id
== NUM_9
|| id
== NUM_0
|| id
== NUM_D
|| id
== NUM_DEC
)
4011 if (Np
->num_curr
< Np
->num_pre
&&
4012 (Np
->Num
->zero_start
> Np
->num_curr
|| !IS_ZERO(Np
->Num
)))
4017 if (!IS_FILLMODE(Np
->Num
))
4019 *Np
->inout_p
= ' '; /* Write ' ' */
4023 else if (IS_ZERO(Np
->Num
) &&
4024 Np
->num_curr
< Np
->num_pre
&&
4025 Np
->Num
->zero_start
<= Np
->num_curr
)
4030 *Np
->inout_p
= '0'; /* Write '0' */
4037 * Write Decimal point
4039 if (*Np
->number_p
== '.')
4041 if (!Np
->last_relevant
|| *Np
->last_relevant
!= '.')
4043 strcpy(Np
->inout_p
, Np
->decimal
); /* Write DEC/D */
4044 Np
->inout_p
+= strlen(Np
->inout_p
);
4048 * Ora 'n' -- FM9.9 --> 'n.'
4050 else if (IS_FILLMODE(Np
->Num
) &&
4051 Np
->last_relevant
&& *Np
->last_relevant
== '.')
4053 strcpy(Np
->inout_p
, Np
->decimal
); /* Write DEC/D */
4054 Np
->inout_p
+= strlen(Np
->inout_p
);
4062 if (Np
->last_relevant
&& Np
->number_p
> Np
->last_relevant
&&
4067 * '0.1' -- 9.9 --> ' .1'
4069 else if (IS_PREDEC_SPACE(Np
))
4071 if (!IS_FILLMODE(Np
->Num
))
4078 * '0' -- FM9.9 --> '0.'
4080 else if (Np
->last_relevant
&& *Np
->last_relevant
== '.')
4088 *Np
->inout_p
= *Np
->number_p
; /* Write DIGIT */
4096 end
= Np
->num_count
+ (Np
->num_pre
? 1 : 0) + (IS_DECIMAL(Np
->Num
) ? 1 : 0);
4098 if (Np
->last_relevant
&& Np
->last_relevant
== Np
->number_p
)
4101 if (Np
->num_curr
+ 1 == end
)
4103 if (Np
->sign_wrote
== TRUE
&& IS_BRACKET(Np
->Num
))
4105 *Np
->inout_p
= Np
->sign
== '+' ? ' ' : '>';
4108 else if (IS_LSIGN(Np
->Num
) && Np
->Num
->lsign
== NUM_LSIGN_POST
)
4110 if (Np
->sign
== '-')
4111 strcpy(Np
->inout_p
, Np
->L_negative_sign
);
4113 strcpy(Np
->inout_p
, Np
->L_positive_sign
);
4114 Np
->inout_p
+= strlen(Np
->inout_p
);
4123 * Note: 'plen' is used in FROM_CHAR conversion and it's length of
4124 * input (inout). In TO_CHAR conversion it's space before first number.
4127 NUM_processor(FormatNode
*node
, NUMDesc
*Num
, char *inout
, char *number
,
4128 int plen
, int sign
, bool is_to_char
)
4134 MemSet(Np
, 0, sizeof(NUMProc
));
4137 Np
->is_to_char
= is_to_char
;
4138 Np
->number
= number
;
4140 Np
->last_relevant
= NULL
;
4143 Np
->read_dec
= FALSE
;
4145 if (Np
->Num
->zero_start
)
4146 --Np
->Num
->zero_start
;
4151 if (IS_ROMAN(Np
->Num
))
4153 if (!Np
->is_to_char
)
4155 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4156 errmsg("\"RN\" not supported")));
4158 Np
->Num
->lsign
= Np
->Num
->pre_lsign_num
= Np
->Num
->post
=
4159 Np
->Num
->pre
= Np
->num_pre
= Np
->sign
= 0;
4161 if (IS_FILLMODE(Np
->Num
))
4164 Np
->Num
->flag
|= NUM_F_FILLMODE
;
4168 Np
->Num
->flag
|= NUM_F_ROMAN
;
4178 /* MI/PL/SG - write sign itself and not in number */
4179 if (IS_PLUS(Np
->Num
) || IS_MINUS(Np
->Num
))
4181 if (IS_PLUS(Np
->Num
) && IS_MINUS(Np
->Num
) == FALSE
)
4182 Np
->sign_wrote
= FALSE
; /* need sign */
4184 Np
->sign_wrote
= TRUE
; /* needn't sign */
4188 if (Np
->sign
!= '-')
4190 if (IS_BRACKET(Np
->Num
) && IS_FILLMODE(Np
->Num
))
4191 Np
->Num
->flag
&= ~NUM_F_BRACKET
;
4192 if (IS_MINUS(Np
->Num
))
4193 Np
->Num
->flag
&= ~NUM_F_MINUS
;
4195 else if (Np
->sign
!= '+' && IS_PLUS(Np
->Num
))
4196 Np
->Num
->flag
&= ~NUM_F_PLUS
;
4198 if (Np
->sign
== '+' && IS_FILLMODE(Np
->Num
) && IS_LSIGN(Np
->Num
) == FALSE
)
4199 Np
->sign_wrote
= TRUE
; /* needn't sign */
4201 Np
->sign_wrote
= FALSE
; /* need sign */
4203 if (Np
->Num
->lsign
== NUM_LSIGN_PRE
&& Np
->Num
->pre
== Np
->Num
->pre_lsign_num
)
4204 Np
->Num
->lsign
= NUM_LSIGN_POST
;
4213 Np
->num_count
= Np
->Num
->post
+ Np
->Num
->pre
- 1;
4219 if (IS_FILLMODE(Np
->Num
))
4221 if (IS_DECIMAL(Np
->Num
))
4222 Np
->last_relevant
= get_last_relevant_decnum(
4224 ((Np
->Num
->zero_end
- Np
->num_pre
> 0) ?
4225 Np
->Num
->zero_end
- Np
->num_pre
: 0));
4228 if (Np
->sign_wrote
== FALSE
&& Np
->num_pre
== 0)
4234 *Np
->number
= ' '; /* sign space */
4235 *(Np
->number
+ 1) = '\0';
4241 #ifdef DEBUG_TO_FROM_CHAR
4242 elog(DEBUG_elog_output
,
4243 "\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",
4250 Np
->sign_wrote
? "Yes" : "No",
4251 IS_ZERO(Np
->Num
) ? "Yes" : "No",
4252 Np
->Num
->zero_start
,
4254 Np
->last_relevant
? Np
->last_relevant
: "<not set>",
4255 IS_BRACKET(Np
->Num
) ? "Yes" : "No",
4256 IS_PLUS(Np
->Num
) ? "Yes" : "No",
4257 IS_MINUS(Np
->Num
) ? "Yes" : "No",
4258 IS_FILLMODE(Np
->Num
) ? "Yes" : "No",
4259 IS_ROMAN(Np
->Num
) ? "Yes" : "No"
4266 NUM_prepare_locale(Np
);
4269 * Processor direct cycle
4272 Np
->number_p
= Np
->number
;
4274 Np
->number_p
= Np
->number
+ 1; /* first char is space for sign */
4276 for (n
= node
, Np
->inout_p
= Np
->inout
; n
->type
!= NODE_TYPE_END
; n
++)
4278 if (!Np
->is_to_char
)
4281 * Check non-string inout end
4283 if (Np
->inout_p
>= Np
->inout
+ plen
)
4288 * Format pictures actions
4290 if (n
->type
== NODE_TYPE_ACTION
)
4293 * Create/reading digit/zero/blank/sing
4295 * 'NUM_S' note: The locale sign is anchored to number and we
4296 * read/write it when we work with first or last number
4297 * (NUM_0/NUM_9). This is reason why NUM_S missing in follow
4308 NUM_numpart_to_char(Np
, n
->key
->id
);
4309 continue; /* for() */
4313 NUM_numpart_from_char(Np
, n
->key
->id
, plen
);
4314 break; /* switch() case: */
4322 if (IS_FILLMODE(Np
->Num
))
4334 if (IS_FILLMODE(Np
->Num
))
4345 if (IS_FILLMODE(Np
->Num
))
4349 int x
= strlen(Np
->L_thousands_sep
);
4351 memset(Np
->inout_p
, ' ', x
);
4352 Np
->inout_p
+= x
- 1;
4357 strcpy(Np
->inout_p
, Np
->L_thousands_sep
);
4358 Np
->inout_p
+= strlen(Np
->inout_p
) - 1;
4365 if (IS_FILLMODE(Np
->Num
))
4368 Np
->inout_p
+= strlen(Np
->L_thousands_sep
) - 1;
4375 strcpy(Np
->inout_p
, Np
->L_currency_symbol
);
4376 Np
->inout_p
+= strlen(Np
->inout_p
) - 1;
4379 Np
->inout_p
+= strlen(Np
->L_currency_symbol
) - 1;
4383 if (IS_FILLMODE(Np
->Num
))
4385 strcpy(Np
->inout_p
, Np
->number_p
);
4386 Np
->inout_p
+= strlen(Np
->inout_p
) - 1;
4390 sprintf(Np
->inout_p
, "%15s", Np
->number_p
);
4391 Np
->inout_p
+= strlen(Np
->inout_p
) - 1;
4396 if (IS_FILLMODE(Np
->Num
))
4398 strcpy(Np
->inout_p
, str_tolower_z(Np
->number_p
));
4399 Np
->inout_p
+= strlen(Np
->inout_p
) - 1;
4403 sprintf(Np
->inout_p
, "%15s", str_tolower_z(Np
->number_p
));
4404 Np
->inout_p
+= strlen(Np
->inout_p
) - 1;
4409 if (IS_ROMAN(Np
->Num
) || *Np
->number
== '#' ||
4410 Np
->sign
== '-' || IS_DECIMAL(Np
->Num
))
4414 strcpy(Np
->inout_p
, get_th(Np
->number
, TH_LOWER
));
4419 if (IS_ROMAN(Np
->Num
) || *Np
->number
== '#' ||
4420 Np
->sign
== '-' || IS_DECIMAL(Np
->Num
))
4424 strcpy(Np
->inout_p
, get_th(Np
->number
, TH_UPPER
));
4431 if (Np
->sign
== '-')
4433 else if (IS_FILLMODE(Np
->Num
))
4440 if (*Np
->inout_p
== '-')
4448 if (Np
->sign
== '+')
4450 else if (IS_FILLMODE(Np
->Num
))
4457 if (*Np
->inout_p
== '+')
4464 *Np
->inout_p
= Np
->sign
;
4468 if (*Np
->inout_p
== '-')
4470 else if (*Np
->inout_p
== '+')
4484 * Remove to output char from input in TO_CHAR
4487 *Np
->inout_p
= n
->character
;
4494 *Np
->inout_p
= '\0';
4499 if (*(Np
->number_p
- 1) == '.')
4500 *(Np
->number_p
- 1) = '\0';
4502 *Np
->number_p
= '\0';
4505 * Correction - precision of dec. number
4507 Np
->Num
->post
= Np
->read_post
;
4509 #ifdef DEBUG_TO_FROM_CHAR
4510 elog(DEBUG_elog_output
, "TO_NUMBER (number): '%s'", Np
->number
);
4517 * MACRO: Start part of NUM - for all NUM's to_char variants
4518 * (sorry, but I hate copy same code - macro is better..)
4521 #define NUM_TOCHAR_prepare \
4523 len = VARSIZE_ANY_EXHDR(fmt); \
4524 if (len <= 0 || len >= (INT_MAX-VARHDRSZ)/NUM_MAX_ITEM_SIZ) \
4525 PG_RETURN_TEXT_P(cstring_to_text("")); \
4526 result = (text *) palloc0((len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ); \
4527 format = NUM_cache(len, &Num, fmt, &shouldFree); \
4531 * MACRO: Finish part of NUM
4534 #define NUM_TOCHAR_finish \
4536 NUM_processor(format, &Num, VARDATA(result), numstr, plen, sign, true); \
4542 * Convert null-terminated representation of result to standard text. \
4543 * The result is usually much bigger than it needs to be, but there \
4544 * seems little point in realloc'ing it smaller. \
4546 len = strlen(VARDATA(result)); \
4547 SET_VARSIZE(result, len + VARHDRSZ); \
4550 /* -------------------
4551 * NUMERIC to_number() (convert string to numeric)
4552 * -------------------
4555 numeric_to_number(PG_FUNCTION_ARGS
)
4557 text
*value
= PG_GETARG_TEXT_P(0);
4558 text
*fmt
= PG_GETARG_TEXT_P(1);
4568 len
= VARSIZE(fmt
) - VARHDRSZ
;
4570 if (len
<= 0 || len
>= INT_MAX
/ NUM_MAX_ITEM_SIZ
)
4573 format
= NUM_cache(len
, &Num
, fmt
, &shouldFree
);
4575 numstr
= (char *) palloc((len
* NUM_MAX_ITEM_SIZ
) + 1);
4577 NUM_processor(format
, &Num
, VARDATA(value
), numstr
,
4578 VARSIZE(value
) - VARHDRSZ
, 0, false);
4581 precision
= Max(0, Num
.pre
) + scale
;
4586 result
= DirectFunctionCall3(numeric_in
,
4587 CStringGetDatum(numstr
),
4588 ObjectIdGetDatum(InvalidOid
),
4589 Int32GetDatum(((precision
<< 16) | scale
) + VARHDRSZ
));
4594 /* ------------------
4596 * ------------------
4599 numeric_to_char(PG_FUNCTION_ARGS
)
4601 Numeric value
= PG_GETARG_NUMERIC(0);
4602 text
*fmt
= PG_GETARG_TEXT_P(1);
4618 * On DateType depend part (numeric)
4622 x
= DatumGetNumeric(DirectFunctionCall2(numeric_round
,
4623 NumericGetDatum(value
),
4626 int_to_roman(DatumGetInt32(DirectFunctionCall1(numeric_int4
,
4627 NumericGetDatum(x
))));
4631 Numeric val
= value
;
4635 Numeric a
= DatumGetNumeric(DirectFunctionCall1(int4_numeric
,
4636 Int32GetDatum(10)));
4637 Numeric b
= DatumGetNumeric(DirectFunctionCall1(int4_numeric
,
4638 Int32GetDatum(Num
.multi
)));
4640 x
= DatumGetNumeric(DirectFunctionCall2(numeric_power
,
4642 NumericGetDatum(b
)));
4643 val
= DatumGetNumeric(DirectFunctionCall2(numeric_mul
,
4644 NumericGetDatum(value
),
4645 NumericGetDatum(x
)));
4646 Num
.pre
+= Num
.multi
;
4649 x
= DatumGetNumeric(DirectFunctionCall2(numeric_round
,
4650 NumericGetDatum(val
),
4651 Int32GetDatum(Num
.post
)));
4652 orgnum
= DatumGetCString(DirectFunctionCall1(numeric_out
,
4653 NumericGetDatum(x
)));
4658 numstr
= orgnum
+ 1;
4665 if ((p
= strchr(numstr
, '.')))
4668 len
= strlen(numstr
);
4671 plen
= Num
.pre
- len
;
4672 else if (len
> Num
.pre
)
4674 numstr
= (char *) palloc(Num
.pre
+ Num
.post
+ 2);
4675 fill_str(numstr
, '#', Num
.pre
+ Num
.post
+ 1);
4676 *(numstr
+ Num
.pre
) = '.';
4681 PG_RETURN_TEXT_P(result
);
4689 int4_to_char(PG_FUNCTION_ARGS
)
4691 int32 value
= PG_GETARG_INT32(0);
4692 text
*fmt
= PG_GETARG_TEXT_P(1);
4706 * On DateType depend part (int32)
4709 numstr
= orgnum
= int_to_roman(value
);
4714 orgnum
= DatumGetCString(DirectFunctionCall1(int4out
,
4715 Int32GetDatum(value
* ((int32
) pow((double) 10, (double) Num
.multi
)))));
4716 Num
.pre
+= Num
.multi
;
4720 orgnum
= DatumGetCString(DirectFunctionCall1(int4out
,
4721 Int32GetDatum(value
)));
4731 len
= strlen(orgnum
);
4735 numstr
= (char *) palloc(len
+ Num
.post
+ 2);
4736 strcpy(numstr
, orgnum
);
4737 *(numstr
+ len
) = '.';
4738 memset(numstr
+ len
+ 1, '0', Num
.post
);
4739 *(numstr
+ len
+ Num
.post
+ 1) = '\0';
4745 plen
= Num
.pre
- len
;
4746 else if (len
> Num
.pre
)
4748 numstr
= (char *) palloc(Num
.pre
+ Num
.post
+ 2);
4749 fill_str(numstr
, '#', Num
.pre
+ Num
.post
+ 1);
4750 *(numstr
+ Num
.pre
) = '.';
4755 PG_RETURN_TEXT_P(result
);
4763 int8_to_char(PG_FUNCTION_ARGS
)
4765 int64 value
= PG_GETARG_INT64(0);
4766 text
*fmt
= PG_GETARG_TEXT_P(1);
4780 * On DateType depend part (int32)
4784 /* Currently don't support int8 conversion to roman... */
4785 numstr
= orgnum
= int_to_roman(DatumGetInt32(
4786 DirectFunctionCall1(int84
, Int64GetDatum(value
))));
4792 double multi
= pow((double) 10, (double) Num
.multi
);
4794 value
= DatumGetInt64(DirectFunctionCall2(int8mul
,
4795 Int64GetDatum(value
),
4796 DirectFunctionCall1(dtoi8
,
4797 Float8GetDatum(multi
))));
4798 Num
.pre
+= Num
.multi
;
4801 orgnum
= DatumGetCString(DirectFunctionCall1(int8out
,
4802 Int64GetDatum(value
)));
4811 len
= strlen(orgnum
);
4815 numstr
= (char *) palloc(len
+ Num
.post
+ 2);
4816 strcpy(numstr
, orgnum
);
4817 *(numstr
+ len
) = '.';
4818 memset(numstr
+ len
+ 1, '0', Num
.post
);
4819 *(numstr
+ len
+ Num
.post
+ 1) = '\0';
4825 plen
= Num
.pre
- len
;
4826 else if (len
> Num
.pre
)
4828 numstr
= (char *) palloc(Num
.pre
+ Num
.post
+ 2);
4829 fill_str(numstr
, '#', Num
.pre
+ Num
.post
+ 1);
4830 *(numstr
+ Num
.pre
) = '.';
4835 PG_RETURN_TEXT_P(result
);
4838 /* -----------------
4843 float4_to_char(PG_FUNCTION_ARGS
)
4845 float4 value
= PG_GETARG_FLOAT4(0);
4846 text
*fmt
= PG_GETARG_TEXT_P(1);
4861 numstr
= orgnum
= int_to_roman((int) rint(value
));
4868 float multi
= pow((double) 10, (double) Num
.multi
);
4870 val
= value
* multi
;
4871 Num
.pre
+= Num
.multi
;
4874 orgnum
= (char *) palloc(MAXFLOATWIDTH
+ 1);
4875 snprintf(orgnum
, MAXFLOATWIDTH
+ 1, "%.0f", fabs(val
));
4876 len
= strlen(orgnum
);
4878 plen
= Num
.pre
- len
;
4881 else if (Num
.post
+ len
> FLT_DIG
)
4882 Num
.post
= FLT_DIG
- len
;
4883 snprintf(orgnum
, MAXFLOATWIDTH
+ 1, "%.*f", Num
.post
, val
);
4888 numstr
= orgnum
+ 1;
4895 if ((p
= strchr(numstr
, '.')))
4898 len
= strlen(numstr
);
4901 plen
= Num
.pre
- len
;
4902 else if (len
> Num
.pre
)
4904 numstr
= (char *) palloc(Num
.pre
+ Num
.post
+ 2);
4905 fill_str(numstr
, '#', Num
.pre
+ Num
.post
+ 1);
4906 *(numstr
+ Num
.pre
) = '.';
4911 PG_RETURN_TEXT_P(result
);
4914 /* -----------------
4919 float8_to_char(PG_FUNCTION_ARGS
)
4921 float8 value
= PG_GETARG_FLOAT8(0);
4922 text
*fmt
= PG_GETARG_TEXT_P(1);
4937 numstr
= orgnum
= int_to_roman((int) rint(value
));
4944 double multi
= pow((double) 10, (double) Num
.multi
);
4946 val
= value
* multi
;
4947 Num
.pre
+= Num
.multi
;
4949 orgnum
= (char *) palloc(MAXDOUBLEWIDTH
+ 1);
4950 len
= snprintf(orgnum
, MAXDOUBLEWIDTH
+ 1, "%.0f", fabs(val
));
4952 plen
= Num
.pre
- len
;
4955 else if (Num
.post
+ len
> DBL_DIG
)
4956 Num
.post
= DBL_DIG
- len
;
4957 snprintf(orgnum
, MAXDOUBLEWIDTH
+ 1, "%.*f", Num
.post
, val
);
4962 numstr
= orgnum
+ 1;
4969 if ((p
= strchr(numstr
, '.')))
4972 len
= strlen(numstr
);
4975 plen
= Num
.pre
- len
;
4976 else if (len
> Num
.pre
)
4978 numstr
= (char *) palloc(Num
.pre
+ Num
.post
+ 2);
4979 fill_str(numstr
, '#', Num
.pre
+ Num
.post
+ 1);
4980 *(numstr
+ Num
.pre
) = '.';
4985 PG_RETURN_TEXT_P(result
);