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