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