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