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