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