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