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