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