Branch data Line data Source code
1 : : /*
2 : : * cash.c
3 : : * Written by D'Arcy J.M. Cain
4 : : * darcy@druid.net
5 : : * http://www.druid.net/darcy/
6 : : *
7 : : * Functions to allow input and output of money normally but store
8 : : * and handle it as 64 bit ints
9 : : *
10 : : * A slightly modified version of this file and a discussion of the
11 : : * workings can be found in the book "Software Solutions in C" by
12 : : * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7 except that
13 : : * this version handles 64 bit numbers and so can hold values up to
14 : : * $92,233,720,368,547,758.07.
15 : : *
16 : : * src/backend/utils/adt/cash.c
17 : : */
18 : :
19 : : #include "postgres.h"
20 : :
21 : : #include <limits.h>
22 : : #include <ctype.h>
23 : : #include <math.h>
24 : :
25 : : #include "common/int.h"
26 : : #include "libpq/pqformat.h"
27 : : #include "nodes/miscnodes.h"
28 : : #include "utils/builtins.h"
29 : : #include "utils/cash.h"
30 : : #include "utils/float.h"
31 : : #include "utils/numeric.h"
32 : : #include "utils/pg_locale.h"
33 : :
34 : :
35 : : /*************************************************************************
36 : : * Private routines
37 : : ************************************************************************/
38 : :
39 : : static void
40 : 16 : append_num_word(StringInfo buf, Cash value)
41 : : {
42 : : static const char *const small[] = {
43 : : "zero", "one", "two", "three", "four", "five", "six", "seven",
44 : : "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
45 : : "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
46 : : "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"
47 : : };
48 : 16 : const char *const *big = small + 18;
49 : 16 : int tu = value % 100;
50 : :
51 : : /* deal with the simple cases first */
52 [ + + ]: 16 : if (value <= 20)
53 : : {
54 : 4 : appendStringInfoString(buf, small[value]);
55 : 4 : return;
56 : : }
57 : :
58 : : /* is it an even multiple of 100? */
59 [ - + ]: 12 : if (!tu)
60 : : {
61 : 0 : appendStringInfo(buf, "%s hundred", small[value / 100]);
62 : 0 : return;
63 : : }
64 : :
65 : : /* more than 99? */
66 [ + + ]: 12 : if (value > 99)
67 : : {
68 : : /* is it an even multiple of 10 other than 10? */
69 [ - + - - ]: 8 : if (value % 10 == 0 && tu > 10)
70 : 0 : appendStringInfo(buf, "%s hundred %s",
71 : 0 : small[value / 100], big[tu / 10]);
72 [ - + ]: 8 : else if (tu < 20)
73 : 0 : appendStringInfo(buf, "%s hundred and %s",
74 : 0 : small[value / 100], small[tu]);
75 : : else
76 : 8 : appendStringInfo(buf, "%s hundred %s %s",
77 : 8 : small[value / 100], big[tu / 10], small[tu % 10]);
78 : : }
79 : : else
80 : : {
81 : : /* is it an even multiple of 10 other than 10? */
82 [ - + - - ]: 4 : if (value % 10 == 0 && tu > 10)
83 : 0 : appendStringInfoString(buf, big[tu / 10]);
84 [ - + ]: 4 : else if (tu < 20)
85 : 0 : appendStringInfoString(buf, small[tu]);
86 : : else
87 : 4 : appendStringInfo(buf, "%s %s", big[tu / 10], small[tu % 10]);
88 : : }
89 : : }
90 : :
91 : : static inline Cash
92 : 20 : cash_pl_cash(Cash c1, Cash c2)
93 : : {
94 : : Cash res;
95 : :
96 [ + + ]: 20 : if (unlikely(pg_add_s64_overflow(c1, c2, &res)))
97 [ + - ]: 4 : ereport(ERROR,
98 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
99 : : errmsg("money out of range")));
100 : :
101 : 16 : return res;
102 : : }
103 : :
104 : : static inline Cash
105 : 12 : cash_mi_cash(Cash c1, Cash c2)
106 : : {
107 : : Cash res;
108 : :
109 [ + + ]: 12 : if (unlikely(pg_sub_s64_overflow(c1, c2, &res)))
110 [ + - ]: 4 : ereport(ERROR,
111 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
112 : : errmsg("money out of range")));
113 : :
114 : 8 : return res;
115 : : }
116 : :
117 : : static inline Cash
118 : 32 : cash_mul_float8(Cash c, float8 f)
119 : : {
120 : 32 : float8 res = rint(float8_mul((float8) c, f));
121 : :
122 [ + + + + : 32 : if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
+ + + + +
+ ]
123 [ + - ]: 16 : ereport(ERROR,
124 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
125 : : errmsg("money out of range")));
126 : :
127 : 16 : return (Cash) res;
128 : : }
129 : :
130 : : static inline Cash
131 : 22 : cash_div_float8(Cash c, float8 f)
132 : : {
133 : 22 : float8 res = rint(float8_div((float8) c, f));
134 : :
135 [ + - + + : 22 : if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
- + + + +
+ ]
136 [ + - ]: 4 : ereport(ERROR,
137 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
138 : : errmsg("money out of range")));
139 : :
140 : 18 : return (Cash) res;
141 : : }
142 : :
143 : : static inline Cash
144 : 32 : cash_mul_int64(Cash c, int64 i)
145 : : {
146 : : Cash res;
147 : :
148 [ + + ]: 32 : if (unlikely(pg_mul_s64_overflow(c, i, &res)))
149 [ + - ]: 8 : ereport(ERROR,
150 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
151 : : errmsg("money out of range")));
152 : :
153 : 24 : return res;
154 : : }
155 : :
156 : : static inline Cash
157 : 58 : cash_div_int64(Cash c, int64 i)
158 : : {
159 : : Cash res;
160 : :
161 [ + + ]: 58 : if (unlikely(i == 0))
162 [ + - ]: 4 : ereport(ERROR,
163 : : (errcode(ERRCODE_DIVISION_BY_ZERO),
164 : : errmsg("division by zero")));
165 : :
166 : : /*
167 : : * INT64_MIN / -1 is problematic, since the result can't be represented on
168 : : * a two's-complement machine. Some machines produce INT64_MIN, some
169 : : * produce zero, some throw an exception. We can dodge the problem by
170 : : * recognizing that division by -1 is the same as negation.
171 : : */
172 [ + + ]: 54 : if (i == -1)
173 : : {
174 [ + - ]: 12 : if (pg_neg_s64_overflow(c, &res))
175 [ + - ]: 12 : ereport(ERROR,
176 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
177 : : errmsg("money out of range")));
178 : 0 : return res;
179 : : }
180 : :
181 : : /* No overflow is possible */
182 : 42 : return c / i;
183 : : }
184 : :
185 : : /*
186 : : * cash_in()
187 : : * Convert a string to a cash data type.
188 : : * Format is [$]###[,]###[.##]
189 : : * Examples: 123.45 $123.45 $123,456.78
190 : : *
191 : : */
192 : : Datum
193 : 895 : cash_in(PG_FUNCTION_ARGS)
194 : : {
195 : 895 : char *str = PG_GETARG_CSTRING(0);
196 : 895 : Node *escontext = fcinfo->context;
197 : : Cash result;
198 : 895 : Cash value = 0;
199 : 895 : Cash dec = 0;
200 : 895 : Cash sgn = 1;
201 : 895 : bool seen_dot = false;
202 : 895 : const char *s = str;
203 : : int fpoint;
204 : : char dsymbol;
205 : : const char *ssymbol,
206 : : *psymbol,
207 : : *nsymbol,
208 : : *csymbol;
209 : 895 : struct lconv *lconvert = PGLC_localeconv();
210 : :
211 : : /*
212 : : * frac_digits will be CHAR_MAX in some locales, notably C. However, just
213 : : * testing for == CHAR_MAX is risky, because of compilers like gcc that
214 : : * "helpfully" let you alter the platform-standard definition of whether
215 : : * char is signed or not. If we are so unfortunate as to get compiled
216 : : * with a nonstandard -fsigned-char or -funsigned-char switch, then our
217 : : * idea of CHAR_MAX will not agree with libc's. The safest course is not
218 : : * to test for CHAR_MAX at all, but to impose a range check for plausible
219 : : * frac_digits values.
220 : : */
221 : 895 : fpoint = lconvert->frac_digits;
222 [ - + - - ]: 895 : if (fpoint < 0 || fpoint > 10)
223 : 895 : fpoint = 2; /* best guess in this case, I think */
224 : :
225 : : /* we restrict dsymbol to be a single byte, but not the other symbols */
226 [ - + ]: 895 : if (*lconvert->mon_decimal_point != '\0' &&
227 [ # # ]: 0 : lconvert->mon_decimal_point[1] == '\0')
228 : 0 : dsymbol = *lconvert->mon_decimal_point;
229 : : else
230 : 895 : dsymbol = '.';
231 [ - + ]: 895 : if (*lconvert->mon_thousands_sep != '\0')
232 : 0 : ssymbol = lconvert->mon_thousands_sep;
233 : : else /* ssymbol should not equal dsymbol */
234 [ + - ]: 895 : ssymbol = (dsymbol != ',') ? "," : ".";
235 [ - + ]: 895 : csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
236 [ - + ]: 895 : psymbol = (*lconvert->positive_sign != '\0') ? lconvert->positive_sign : "+";
237 [ - + ]: 895 : nsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
238 : :
239 : : #ifdef CASHDEBUG
240 : : printf("cashin- precision '%d'; decimal '%c'; thousands '%s'; currency '%s'; positive '%s'; negative '%s'\n",
241 : : fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
242 : : #endif
243 : :
244 : : /* we need to add all sorts of checking here. For now just */
245 : : /* strip all leading whitespace and any leading currency symbol */
246 [ - + ]: 895 : while (isspace((unsigned char) *s))
247 : 0 : s++;
248 [ + + ]: 895 : if (strncmp(s, csymbol, strlen(csymbol)) == 0)
249 : 80 : s += strlen(csymbol);
250 [ - + ]: 895 : while (isspace((unsigned char) *s))
251 : 0 : s++;
252 : :
253 : : #ifdef CASHDEBUG
254 : : printf("cashin- string is '%s'\n", s);
255 : : #endif
256 : :
257 : : /* a leading minus or paren signifies a negative number */
258 : : /* again, better heuristics needed */
259 : : /* XXX - doesn't properly check for balanced parens - djmc */
260 [ + + ]: 895 : if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
261 : : {
262 : 339 : sgn = -1;
263 : 339 : s += strlen(nsymbol);
264 : : }
265 [ + + ]: 556 : else if (*s == '(')
266 : : {
267 : 8 : sgn = -1;
268 : 8 : s++;
269 : : }
270 [ - + ]: 548 : else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
271 : 0 : s += strlen(psymbol);
272 : :
273 : : #ifdef CASHDEBUG
274 : : printf("cashin- string is '%s'\n", s);
275 : : #endif
276 : :
277 : : /* allow whitespace and currency symbol after the sign, too */
278 [ - + ]: 895 : while (isspace((unsigned char) *s))
279 : 0 : s++;
280 [ + + ]: 895 : if (strncmp(s, csymbol, strlen(csymbol)) == 0)
281 : 4 : s += strlen(csymbol);
282 [ - + ]: 895 : while (isspace((unsigned char) *s))
283 : 0 : s++;
284 : :
285 : : #ifdef CASHDEBUG
286 : : printf("cashin- string is '%s'\n", s);
287 : : #endif
288 : :
289 : : /*
290 : : * We accumulate the absolute amount in "value" and then apply the sign at
291 : : * the end. (The sign can appear before or after the digits, so it would
292 : : * be more complicated to do otherwise.) Because of the larger range of
293 : : * negative signed integers, we build "value" in the negative and then
294 : : * flip the sign at the end, catching most-negative-number overflow if
295 : : * necessary.
296 : : */
297 : :
298 [ + + ]: 9054 : for (; *s; s++)
299 : : {
300 : : /*
301 : : * We look for digits as long as we have found less than the required
302 : : * number of decimal places.
303 : : */
304 [ + + + + : 8215 : if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
+ + ]
305 : 7368 : {
306 : 7380 : int8 digit = *s - '0';
307 : :
308 [ + + + + ]: 14752 : if (pg_mul_s64_overflow(value, 10, &value) ||
309 : 7372 : pg_sub_s64_overflow(value, digit, &value))
310 [ + + ]: 12 : ereturn(escontext, (Datum) 0,
311 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
312 : : errmsg("value \"%s\" is out of range for type %s",
313 : : str, "money")));
314 : :
315 [ + + ]: 7368 : if (seen_dot)
316 : 1562 : dec++;
317 : : }
318 : : /* decimal point? then start counting fractions... */
319 [ + + + - ]: 835 : else if (*s == dsymbol && !seen_dot)
320 : : {
321 : 787 : seen_dot = true;
322 : : }
323 : : /* ignore if "thousands" separator, else we're done */
324 [ + + ]: 48 : else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
325 : 4 : s += strlen(ssymbol) - 1;
326 : : else
327 : 44 : break;
328 : : }
329 : :
330 : : /* round off if there's another digit */
331 [ + + + + ]: 883 : if (isdigit((unsigned char) *s) && *s >= '5')
332 : : {
333 : : /* remember we build the value in the negative */
334 [ + + ]: 20 : if (pg_sub_s64_overflow(value, 1, &value))
335 [ + - ]: 4 : ereturn(escontext, (Datum) 0,
336 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
337 : : errmsg("value \"%s\" is out of range for type %s",
338 : : str, "money")));
339 : : }
340 : :
341 : : /* adjust for less than required decimal places */
342 [ + + ]: 1071 : for (; dec < fpoint; dec++)
343 : : {
344 [ + + ]: 208 : if (pg_mul_s64_overflow(value, 10, &value))
345 [ + - ]: 16 : ereturn(escontext, (Datum) 0,
346 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
347 : : errmsg("value \"%s\" is out of range for type %s",
348 : : str, "money")));
349 : : }
350 : :
351 : : /*
352 : : * should only be trailing digits followed by whitespace, right paren,
353 : : * trailing sign, and/or trailing currency symbol
354 : : */
355 [ + + ]: 887 : while (isdigit((unsigned char) *s))
356 : 24 : s++;
357 : :
358 [ + + ]: 871 : while (*s)
359 : : {
360 [ + - + + ]: 16 : if (isspace((unsigned char) *s) || *s == ')')
361 : 8 : s++;
362 [ - + ]: 8 : else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
363 : : {
364 : 0 : sgn = -1;
365 : 0 : s += strlen(nsymbol);
366 : : }
367 [ - + ]: 8 : else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
368 : 0 : s += strlen(psymbol);
369 [ - + ]: 8 : else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
370 : 0 : s += strlen(csymbol);
371 : : else
372 [ + + ]: 8 : ereturn(escontext, (Datum) 0,
373 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
374 : : errmsg("invalid input syntax for type %s: \"%s\"",
375 : : "money", str)));
376 : : }
377 : :
378 : : /*
379 : : * If the value is supposed to be positive, flip the sign, but check for
380 : : * the most negative number.
381 : : */
382 [ + + ]: 855 : if (sgn > 0)
383 : : {
384 [ + + ]: 524 : if (pg_neg_s64_overflow(value, &result))
385 [ + - ]: 8 : ereturn(escontext, (Datum) 0,
386 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
387 : : errmsg("value \"%s\" is out of range for type %s",
388 : : str, "money")));
389 : : }
390 : : else
391 : 331 : result = value;
392 : :
393 : : #ifdef CASHDEBUG
394 : : printf("cashin- result is " INT64_FORMAT "\n", result);
395 : : #endif
396 : :
397 : 847 : PG_RETURN_CASH(result);
398 : : }
399 : :
400 : :
401 : : /*
402 : : * cash_out()
403 : : * Function to convert cash to a dollars and cents representation, using
404 : : * the lc_monetary locale's formatting.
405 : : */
406 : : Datum
407 : 265 : cash_out(PG_FUNCTION_ARGS)
408 : : {
409 : 265 : Cash value = PG_GETARG_CASH(0);
410 : : uint64 uvalue;
411 : : char *result;
412 : : char buf[128];
413 : : char *bufptr;
414 : : int digit_pos;
415 : : int points,
416 : : mon_group;
417 : : char dsymbol;
418 : : const char *ssymbol,
419 : : *csymbol,
420 : : *signsymbol;
421 : : char sign_posn,
422 : : cs_precedes,
423 : : sep_by_space;
424 : 265 : struct lconv *lconvert = PGLC_localeconv();
425 : :
426 : : /* see comments about frac_digits in cash_in() */
427 : 265 : points = lconvert->frac_digits;
428 [ - + - - ]: 265 : if (points < 0 || points > 10)
429 : 265 : points = 2; /* best guess in this case, I think */
430 : :
431 : : /*
432 : : * As with frac_digits, must apply a range check to mon_grouping to avoid
433 : : * being fooled by variant CHAR_MAX values.
434 : : */
435 : 265 : mon_group = *lconvert->mon_grouping;
436 [ - + - - ]: 265 : if (mon_group <= 0 || mon_group > 6)
437 : 265 : mon_group = 3;
438 : :
439 : : /* we restrict dsymbol to be a single byte, but not the other symbols */
440 [ - + ]: 265 : if (*lconvert->mon_decimal_point != '\0' &&
441 [ # # ]: 0 : lconvert->mon_decimal_point[1] == '\0')
442 : 0 : dsymbol = *lconvert->mon_decimal_point;
443 : : else
444 : 265 : dsymbol = '.';
445 [ - + ]: 265 : if (*lconvert->mon_thousands_sep != '\0')
446 : 0 : ssymbol = lconvert->mon_thousands_sep;
447 : : else /* ssymbol should not equal dsymbol */
448 [ + - ]: 265 : ssymbol = (dsymbol != ',') ? "," : ".";
449 [ - + ]: 265 : csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
450 : :
451 [ + + ]: 265 : if (value < 0)
452 : : {
453 : : /* set up formatting data */
454 [ - + ]: 56 : signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
455 : 56 : sign_posn = lconvert->n_sign_posn;
456 : 56 : cs_precedes = lconvert->n_cs_precedes;
457 : 56 : sep_by_space = lconvert->n_sep_by_space;
458 : : }
459 : : else
460 : : {
461 : 209 : signsymbol = lconvert->positive_sign;
462 : 209 : sign_posn = lconvert->p_sign_posn;
463 : 209 : cs_precedes = lconvert->p_cs_precedes;
464 : 209 : sep_by_space = lconvert->p_sep_by_space;
465 : : }
466 : :
467 : : /* make the amount positive for digit-reconstruction loop */
468 : 265 : uvalue = pg_abs_s64(value);
469 : :
470 : : /* we build the digits+decimal-point+sep string right-to-left in buf[] */
471 : 265 : bufptr = buf + sizeof(buf) - 1;
472 : 265 : *bufptr = '\0';
473 : :
474 : : /*
475 : : * Generate digits till there are no non-zero digits left and we emitted
476 : : * at least one to the left of the decimal point. digit_pos is the
477 : : * current digit position, with zero as the digit just left of the decimal
478 : : * point, increasing to the right.
479 : : */
480 : 265 : digit_pos = points;
481 : : do
482 : : {
483 [ + - + + ]: 2139 : if (points && digit_pos == 0)
484 : : {
485 : : /* insert decimal point, but not if value cannot be fractional */
486 : 265 : *(--bufptr) = dsymbol;
487 : : }
488 [ + + + + ]: 1874 : else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
489 : : {
490 : : /* insert thousands sep, but only to left of radix point */
491 : 351 : bufptr -= strlen(ssymbol);
492 : 351 : memcpy(bufptr, ssymbol, strlen(ssymbol));
493 : : }
494 : :
495 : 2139 : *(--bufptr) = (uvalue % 10) + '0';
496 : 2139 : uvalue = uvalue / 10;
497 : 2139 : digit_pos--;
498 [ + + + + ]: 2139 : } while (uvalue || digit_pos >= 0);
499 : :
500 : : /*----------
501 : : * Now, attach currency symbol and sign symbol in the correct order.
502 : : *
503 : : * The POSIX spec defines these values controlling this code:
504 : : *
505 : : * p/n_sign_posn:
506 : : * 0 Parentheses enclose the quantity and the currency_symbol.
507 : : * 1 The sign string precedes the quantity and the currency_symbol.
508 : : * 2 The sign string succeeds the quantity and the currency_symbol.
509 : : * 3 The sign string precedes the currency_symbol.
510 : : * 4 The sign string succeeds the currency_symbol.
511 : : *
512 : : * p/n_cs_precedes: 0 means currency symbol after value, else before it.
513 : : *
514 : : * p/n_sep_by_space:
515 : : * 0 No <space> separates the currency symbol and value.
516 : : * 1 If the currency symbol and sign string are adjacent, a <space>
517 : : * separates them from the value; otherwise, a <space> separates
518 : : * the currency symbol from the value.
519 : : * 2 If the currency symbol and sign string are adjacent, a <space>
520 : : * separates them; otherwise, a <space> separates the sign string
521 : : * from the value.
522 : : *----------
523 : : */
524 [ - + - - : 265 : switch (sign_posn)
- ]
525 : : {
526 : 0 : case 0:
527 [ # # ]: 0 : if (cs_precedes)
528 [ # # ]: 0 : result = psprintf("(%s%s%s)",
529 : : csymbol,
530 : : (sep_by_space == 1) ? " " : "",
531 : : bufptr);
532 : : else
533 [ # # ]: 0 : result = psprintf("(%s%s%s)",
534 : : bufptr,
535 : : (sep_by_space == 1) ? " " : "",
536 : : csymbol);
537 : 0 : break;
538 : 265 : case 1:
539 : : default:
540 [ + - ]: 265 : if (cs_precedes)
541 [ - + - + ]: 265 : result = psprintf("%s%s%s%s%s",
542 : : signsymbol,
543 : : (sep_by_space == 2) ? " " : "",
544 : : csymbol,
545 : : (sep_by_space == 1) ? " " : "",
546 : : bufptr);
547 : : else
548 [ # # # # ]: 0 : result = psprintf("%s%s%s%s%s",
549 : : signsymbol,
550 : : (sep_by_space == 2) ? " " : "",
551 : : bufptr,
552 : : (sep_by_space == 1) ? " " : "",
553 : : csymbol);
554 : 265 : break;
555 : 0 : case 2:
556 [ # # ]: 0 : if (cs_precedes)
557 [ # # # # ]: 0 : result = psprintf("%s%s%s%s%s",
558 : : csymbol,
559 : : (sep_by_space == 1) ? " " : "",
560 : : bufptr,
561 : : (sep_by_space == 2) ? " " : "",
562 : : signsymbol);
563 : : else
564 [ # # # # ]: 0 : result = psprintf("%s%s%s%s%s",
565 : : bufptr,
566 : : (sep_by_space == 1) ? " " : "",
567 : : csymbol,
568 : : (sep_by_space == 2) ? " " : "",
569 : : signsymbol);
570 : 0 : break;
571 : 0 : case 3:
572 [ # # ]: 0 : if (cs_precedes)
573 [ # # # # ]: 0 : result = psprintf("%s%s%s%s%s",
574 : : signsymbol,
575 : : (sep_by_space == 2) ? " " : "",
576 : : csymbol,
577 : : (sep_by_space == 1) ? " " : "",
578 : : bufptr);
579 : : else
580 [ # # # # ]: 0 : result = psprintf("%s%s%s%s%s",
581 : : bufptr,
582 : : (sep_by_space == 1) ? " " : "",
583 : : signsymbol,
584 : : (sep_by_space == 2) ? " " : "",
585 : : csymbol);
586 : 0 : break;
587 : 0 : case 4:
588 [ # # ]: 0 : if (cs_precedes)
589 [ # # # # ]: 0 : result = psprintf("%s%s%s%s%s",
590 : : csymbol,
591 : : (sep_by_space == 2) ? " " : "",
592 : : signsymbol,
593 : : (sep_by_space == 1) ? " " : "",
594 : : bufptr);
595 : : else
596 [ # # # # ]: 0 : result = psprintf("%s%s%s%s%s",
597 : : bufptr,
598 : : (sep_by_space == 1) ? " " : "",
599 : : csymbol,
600 : : (sep_by_space == 2) ? " " : "",
601 : : signsymbol);
602 : 0 : break;
603 : : }
604 : :
605 : 265 : PG_RETURN_CSTRING(result);
606 : : }
607 : :
608 : : /*
609 : : * cash_recv - converts external binary format to cash
610 : : */
611 : : Datum
612 : 0 : cash_recv(PG_FUNCTION_ARGS)
613 : : {
614 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
615 : :
616 : 0 : PG_RETURN_CASH((Cash) pq_getmsgint64(buf));
617 : : }
618 : :
619 : : /*
620 : : * cash_send - converts cash to binary format
621 : : */
622 : : Datum
623 : 0 : cash_send(PG_FUNCTION_ARGS)
624 : : {
625 : 0 : Cash arg1 = PG_GETARG_CASH(0);
626 : : StringInfoData buf;
627 : :
628 : 0 : pq_begintypsend(&buf);
629 : 0 : pq_sendint64(&buf, arg1);
630 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
631 : : }
632 : :
633 : : /*
634 : : * Comparison functions
635 : : */
636 : :
637 : : Datum
638 : 552 : cash_eq(PG_FUNCTION_ARGS)
639 : : {
640 : 552 : Cash c1 = PG_GETARG_CASH(0);
641 : 552 : Cash c2 = PG_GETARG_CASH(1);
642 : :
643 : 552 : PG_RETURN_BOOL(c1 == c2);
644 : : }
645 : :
646 : : Datum
647 : 8 : cash_ne(PG_FUNCTION_ARGS)
648 : : {
649 : 8 : Cash c1 = PG_GETARG_CASH(0);
650 : 8 : Cash c2 = PG_GETARG_CASH(1);
651 : :
652 : 8 : PG_RETURN_BOOL(c1 != c2);
653 : : }
654 : :
655 : : Datum
656 : 551 : cash_lt(PG_FUNCTION_ARGS)
657 : : {
658 : 551 : Cash c1 = PG_GETARG_CASH(0);
659 : 551 : Cash c2 = PG_GETARG_CASH(1);
660 : :
661 : 551 : PG_RETURN_BOOL(c1 < c2);
662 : : }
663 : :
664 : : Datum
665 : 551 : cash_le(PG_FUNCTION_ARGS)
666 : : {
667 : 551 : Cash c1 = PG_GETARG_CASH(0);
668 : 551 : Cash c2 = PG_GETARG_CASH(1);
669 : :
670 : 551 : PG_RETURN_BOOL(c1 <= c2);
671 : : }
672 : :
673 : : Datum
674 : 551 : cash_gt(PG_FUNCTION_ARGS)
675 : : {
676 : 551 : Cash c1 = PG_GETARG_CASH(0);
677 : 551 : Cash c2 = PG_GETARG_CASH(1);
678 : :
679 : 551 : PG_RETURN_BOOL(c1 > c2);
680 : : }
681 : :
682 : : Datum
683 : 551 : cash_ge(PG_FUNCTION_ARGS)
684 : : {
685 : 551 : Cash c1 = PG_GETARG_CASH(0);
686 : 551 : Cash c2 = PG_GETARG_CASH(1);
687 : :
688 : 551 : PG_RETURN_BOOL(c1 >= c2);
689 : : }
690 : :
691 : : Datum
692 : 663 : cash_cmp(PG_FUNCTION_ARGS)
693 : : {
694 : 663 : Cash c1 = PG_GETARG_CASH(0);
695 : 663 : Cash c2 = PG_GETARG_CASH(1);
696 : :
697 [ + + ]: 663 : if (c1 > c2)
698 : 561 : PG_RETURN_INT32(1);
699 [ + + ]: 102 : else if (c1 == c2)
700 : 7 : PG_RETURN_INT32(0);
701 : : else
702 : 95 : PG_RETURN_INT32(-1);
703 : : }
704 : :
705 : :
706 : : /*
707 : : * cash_pl()
708 : : * Add two cash values.
709 : : */
710 : : Datum
711 : 20 : cash_pl(PG_FUNCTION_ARGS)
712 : : {
713 : 20 : Cash c1 = PG_GETARG_CASH(0);
714 : 20 : Cash c2 = PG_GETARG_CASH(1);
715 : :
716 : 20 : PG_RETURN_CASH(cash_pl_cash(c1, c2));
717 : : }
718 : :
719 : :
720 : : /*
721 : : * cash_mi()
722 : : * Subtract two cash values.
723 : : */
724 : : Datum
725 : 12 : cash_mi(PG_FUNCTION_ARGS)
726 : : {
727 : 12 : Cash c1 = PG_GETARG_CASH(0);
728 : 12 : Cash c2 = PG_GETARG_CASH(1);
729 : :
730 : 12 : PG_RETURN_CASH(cash_mi_cash(c1, c2));
731 : : }
732 : :
733 : :
734 : : /*
735 : : * cash_div_cash()
736 : : * Divide cash by cash, returning float8.
737 : : */
738 : : Datum
739 : 4 : cash_div_cash(PG_FUNCTION_ARGS)
740 : : {
741 : 4 : Cash dividend = PG_GETARG_CASH(0);
742 : 4 : Cash divisor = PG_GETARG_CASH(1);
743 : : float8 quotient;
744 : :
745 [ - + ]: 4 : if (divisor == 0)
746 [ # # ]: 0 : ereport(ERROR,
747 : : (errcode(ERRCODE_DIVISION_BY_ZERO),
748 : : errmsg("division by zero")));
749 : :
750 : 4 : quotient = (float8) dividend / (float8) divisor;
751 : 4 : PG_RETURN_FLOAT8(quotient);
752 : : }
753 : :
754 : :
755 : : /*
756 : : * cash_mul_flt8()
757 : : * Multiply cash by float8.
758 : : */
759 : : Datum
760 : 16 : cash_mul_flt8(PG_FUNCTION_ARGS)
761 : : {
762 : 16 : Cash c = PG_GETARG_CASH(0);
763 : 16 : float8 f = PG_GETARG_FLOAT8(1);
764 : :
765 : 16 : PG_RETURN_CASH(cash_mul_float8(c, f));
766 : : }
767 : :
768 : :
769 : : /*
770 : : * flt8_mul_cash()
771 : : * Multiply float8 by cash.
772 : : */
773 : : Datum
774 : 4 : flt8_mul_cash(PG_FUNCTION_ARGS)
775 : : {
776 : 4 : float8 f = PG_GETARG_FLOAT8(0);
777 : 4 : Cash c = PG_GETARG_CASH(1);
778 : :
779 : 4 : PG_RETURN_CASH(cash_mul_float8(c, f));
780 : : }
781 : :
782 : :
783 : : /*
784 : : * cash_div_flt8()
785 : : * Divide cash by float8.
786 : : */
787 : : Datum
788 : 9 : cash_div_flt8(PG_FUNCTION_ARGS)
789 : : {
790 : 9 : Cash c = PG_GETARG_CASH(0);
791 : 9 : float8 f = PG_GETARG_FLOAT8(1);
792 : :
793 : 9 : PG_RETURN_CASH(cash_div_float8(c, f));
794 : : }
795 : :
796 : :
797 : : /*
798 : : * cash_mul_flt4()
799 : : * Multiply cash by float4.
800 : : */
801 : : Datum
802 : 8 : cash_mul_flt4(PG_FUNCTION_ARGS)
803 : : {
804 : 8 : Cash c = PG_GETARG_CASH(0);
805 : 8 : float4 f = PG_GETARG_FLOAT4(1);
806 : :
807 : 8 : PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
808 : : }
809 : :
810 : :
811 : : /*
812 : : * flt4_mul_cash()
813 : : * Multiply float4 by cash.
814 : : */
815 : : Datum
816 : 4 : flt4_mul_cash(PG_FUNCTION_ARGS)
817 : : {
818 : 4 : float4 f = PG_GETARG_FLOAT4(0);
819 : 4 : Cash c = PG_GETARG_CASH(1);
820 : :
821 : 4 : PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
822 : : }
823 : :
824 : :
825 : : /*
826 : : * cash_div_flt4()
827 : : * Divide cash by float4.
828 : : *
829 : : */
830 : : Datum
831 : 13 : cash_div_flt4(PG_FUNCTION_ARGS)
832 : : {
833 : 13 : Cash c = PG_GETARG_CASH(0);
834 : 13 : float4 f = PG_GETARG_FLOAT4(1);
835 : :
836 : 13 : PG_RETURN_CASH(cash_div_float8(c, (float8) f));
837 : : }
838 : :
839 : :
840 : : /*
841 : : * cash_mul_int8()
842 : : * Multiply cash by int8.
843 : : */
844 : : Datum
845 : 8 : cash_mul_int8(PG_FUNCTION_ARGS)
846 : : {
847 : 8 : Cash c = PG_GETARG_CASH(0);
848 : 8 : int64 i = PG_GETARG_INT64(1);
849 : :
850 : 8 : PG_RETURN_CASH(cash_mul_int64(c, i));
851 : : }
852 : :
853 : :
854 : : /*
855 : : * int8_mul_cash()
856 : : * Multiply int8 by cash.
857 : : */
858 : : Datum
859 : 4 : int8_mul_cash(PG_FUNCTION_ARGS)
860 : : {
861 : 4 : int64 i = PG_GETARG_INT64(0);
862 : 4 : Cash c = PG_GETARG_CASH(1);
863 : :
864 : 4 : PG_RETURN_CASH(cash_mul_int64(c, i));
865 : : }
866 : :
867 : : /*
868 : : * cash_div_int8()
869 : : * Divide cash by 8-byte integer.
870 : : */
871 : : Datum
872 : 18 : cash_div_int8(PG_FUNCTION_ARGS)
873 : : {
874 : 18 : Cash c = PG_GETARG_CASH(0);
875 : 18 : int64 i = PG_GETARG_INT64(1);
876 : :
877 : 18 : PG_RETURN_CASH(cash_div_int64(c, i));
878 : : }
879 : :
880 : :
881 : : /*
882 : : * cash_mul_int4()
883 : : * Multiply cash by int4.
884 : : */
885 : : Datum
886 : 8 : cash_mul_int4(PG_FUNCTION_ARGS)
887 : : {
888 : 8 : Cash c = PG_GETARG_CASH(0);
889 : 8 : int32 i = PG_GETARG_INT32(1);
890 : :
891 : 8 : PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
892 : : }
893 : :
894 : :
895 : : /*
896 : : * int4_mul_cash()
897 : : * Multiply int4 by cash.
898 : : */
899 : : Datum
900 : 4 : int4_mul_cash(PG_FUNCTION_ARGS)
901 : : {
902 : 4 : int32 i = PG_GETARG_INT32(0);
903 : 4 : Cash c = PG_GETARG_CASH(1);
904 : :
905 : 4 : PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
906 : : }
907 : :
908 : :
909 : : /*
910 : : * cash_div_int4()
911 : : * Divide cash by 4-byte integer.
912 : : *
913 : : */
914 : : Datum
915 : 18 : cash_div_int4(PG_FUNCTION_ARGS)
916 : : {
917 : 18 : Cash c = PG_GETARG_CASH(0);
918 : 18 : int32 i = PG_GETARG_INT32(1);
919 : :
920 : 18 : PG_RETURN_CASH(cash_div_int64(c, (int64) i));
921 : : }
922 : :
923 : :
924 : : /*
925 : : * cash_mul_int2()
926 : : * Multiply cash by int2.
927 : : */
928 : : Datum
929 : 4 : cash_mul_int2(PG_FUNCTION_ARGS)
930 : : {
931 : 4 : Cash c = PG_GETARG_CASH(0);
932 : 4 : int16 s = PG_GETARG_INT16(1);
933 : :
934 : 4 : PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
935 : : }
936 : :
937 : : /*
938 : : * int2_mul_cash()
939 : : * Multiply int2 by cash.
940 : : */
941 : : Datum
942 : 4 : int2_mul_cash(PG_FUNCTION_ARGS)
943 : : {
944 : 4 : int16 s = PG_GETARG_INT16(0);
945 : 4 : Cash c = PG_GETARG_CASH(1);
946 : :
947 : 4 : PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
948 : : }
949 : :
950 : : /*
951 : : * cash_div_int2()
952 : : * Divide cash by int2.
953 : : *
954 : : */
955 : : Datum
956 : 22 : cash_div_int2(PG_FUNCTION_ARGS)
957 : : {
958 : 22 : Cash c = PG_GETARG_CASH(0);
959 : 22 : int16 s = PG_GETARG_INT16(1);
960 : :
961 : 22 : PG_RETURN_CASH(cash_div_int64(c, (int64) s));
962 : : }
963 : :
964 : : /*
965 : : * cashlarger()
966 : : * Return larger of two cash values.
967 : : */
968 : : Datum
969 : 4 : cashlarger(PG_FUNCTION_ARGS)
970 : : {
971 : 4 : Cash c1 = PG_GETARG_CASH(0);
972 : 4 : Cash c2 = PG_GETARG_CASH(1);
973 : : Cash result;
974 : :
975 : 4 : result = (c1 > c2) ? c1 : c2;
976 : :
977 : 4 : PG_RETURN_CASH(result);
978 : : }
979 : :
980 : : /*
981 : : * cashsmaller()
982 : : * Return smaller of two cash values.
983 : : */
984 : : Datum
985 : 4 : cashsmaller(PG_FUNCTION_ARGS)
986 : : {
987 : 4 : Cash c1 = PG_GETARG_CASH(0);
988 : 4 : Cash c2 = PG_GETARG_CASH(1);
989 : : Cash result;
990 : :
991 : 4 : result = (c1 < c2) ? c1 : c2;
992 : :
993 : 4 : PG_RETURN_CASH(result);
994 : : }
995 : :
996 : : /*
997 : : * cash_words()
998 : : * This converts an int4 as well but to a representation using words
999 : : * Obviously way North American centric - sorry
1000 : : */
1001 : : Datum
1002 : 8 : cash_words(PG_FUNCTION_ARGS)
1003 : : {
1004 : 8 : Cash value = PG_GETARG_CASH(0);
1005 : : uint64 val;
1006 : : StringInfoData buf;
1007 : : text *res;
1008 : : Cash dollars;
1009 : : Cash m0;
1010 : : Cash m1;
1011 : : Cash m2;
1012 : : Cash m3;
1013 : : Cash m4;
1014 : : Cash m5;
1015 : : Cash m6;
1016 : :
1017 : 8 : initStringInfo(&buf);
1018 : :
1019 : : /* work with positive numbers */
1020 [ - + ]: 8 : if (value < 0)
1021 : : {
1022 : 0 : value = -value;
1023 : 0 : appendStringInfoString(&buf, "minus ");
1024 : : }
1025 : :
1026 : : /* Now treat as unsigned, to avoid trouble at INT_MIN */
1027 : 8 : val = (uint64) value;
1028 : :
1029 : 8 : dollars = val / INT64CONST(100);
1030 : 8 : m0 = val % INT64CONST(100); /* cents */
1031 : 8 : m1 = (val / INT64CONST(100)) % 1000; /* hundreds */
1032 : 8 : m2 = (val / INT64CONST(100000)) % 1000; /* thousands */
1033 : 8 : m3 = (val / INT64CONST(100000000)) % 1000; /* millions */
1034 : 8 : m4 = (val / INT64CONST(100000000000)) % 1000; /* billions */
1035 : 8 : m5 = (val / INT64CONST(100000000000000)) % 1000; /* trillions */
1036 : 8 : m6 = (val / INT64CONST(100000000000000000)) % 1000; /* quadrillions */
1037 : :
1038 [ - + ]: 8 : if (m6)
1039 : : {
1040 : 0 : append_num_word(&buf, m6);
1041 : 0 : appendStringInfoString(&buf, " quadrillion ");
1042 : : }
1043 : :
1044 [ - + ]: 8 : if (m5)
1045 : : {
1046 : 0 : append_num_word(&buf, m5);
1047 : 0 : appendStringInfoString(&buf, " trillion ");
1048 : : }
1049 : :
1050 [ - + ]: 8 : if (m4)
1051 : : {
1052 : 0 : append_num_word(&buf, m4);
1053 : 0 : appendStringInfoString(&buf, " billion ");
1054 : : }
1055 : :
1056 [ - + ]: 8 : if (m3)
1057 : : {
1058 : 0 : append_num_word(&buf, m3);
1059 : 0 : appendStringInfoString(&buf, " million ");
1060 : : }
1061 : :
1062 [ - + ]: 8 : if (m2)
1063 : : {
1064 : 0 : append_num_word(&buf, m2);
1065 : 0 : appendStringInfoString(&buf, " thousand ");
1066 : : }
1067 : :
1068 [ + - ]: 8 : if (m1)
1069 : 8 : append_num_word(&buf, m1);
1070 : :
1071 [ - + ]: 8 : if (dollars == 0)
1072 : 0 : appendStringInfoString(&buf, "zero");
1073 : :
1074 [ - + ]: 8 : appendStringInfoString(&buf, dollars == 1 ? " dollar and " : " dollars and ");
1075 : 8 : append_num_word(&buf, m0);
1076 [ - + ]: 8 : appendStringInfoString(&buf, m0 == 1 ? " cent" : " cents");
1077 : :
1078 : : /* capitalize output */
1079 : 8 : buf.data[0] = pg_ascii_toupper((unsigned char) buf.data[0]);
1080 : :
1081 : : /* return as text datum */
1082 : 8 : res = cstring_to_text_with_len(buf.data, buf.len);
1083 : 8 : pfree(buf.data);
1084 : 8 : PG_RETURN_TEXT_P(res);
1085 : : }
1086 : :
1087 : :
1088 : : /*
1089 : : * cash_numeric()
1090 : : * Convert cash to numeric.
1091 : : */
1092 : : Datum
1093 : 16 : cash_numeric(PG_FUNCTION_ARGS)
1094 : : {
1095 : 16 : Cash money = PG_GETARG_CASH(0);
1096 : : Datum result;
1097 : : int fpoint;
1098 : 16 : struct lconv *lconvert = PGLC_localeconv();
1099 : :
1100 : : /* see comments about frac_digits in cash_in() */
1101 : 16 : fpoint = lconvert->frac_digits;
1102 [ - + - - ]: 16 : if (fpoint < 0 || fpoint > 10)
1103 : 16 : fpoint = 2;
1104 : :
1105 : : /* convert the integral money value to numeric */
1106 : 16 : result = NumericGetDatum(int64_to_numeric(money));
1107 : :
1108 : : /* scale appropriately, if needed */
1109 [ + - ]: 16 : if (fpoint > 0)
1110 : : {
1111 : : int64 scale;
1112 : : int i;
1113 : : Datum numeric_scale;
1114 : : Datum quotient;
1115 : :
1116 : : /* compute required scale factor */
1117 : 16 : scale = 1;
1118 [ + + ]: 48 : for (i = 0; i < fpoint; i++)
1119 : 32 : scale *= 10;
1120 : 16 : numeric_scale = NumericGetDatum(int64_to_numeric(scale));
1121 : :
1122 : : /*
1123 : : * Given integral inputs approaching INT64_MAX, select_div_scale()
1124 : : * might choose a result scale of zero, causing loss of fractional
1125 : : * digits in the quotient. We can ensure an exact result by setting
1126 : : * the dscale of either input to be at least as large as the desired
1127 : : * result scale. numeric_round() will do that for us.
1128 : : */
1129 : 16 : numeric_scale = DirectFunctionCall2(numeric_round,
1130 : : numeric_scale,
1131 : : Int32GetDatum(fpoint));
1132 : :
1133 : : /* Now we can safely divide ... */
1134 : 16 : quotient = DirectFunctionCall2(numeric_div, result, numeric_scale);
1135 : :
1136 : : /* ... and forcibly round to exactly the intended number of digits */
1137 : 16 : result = DirectFunctionCall2(numeric_round,
1138 : : quotient,
1139 : : Int32GetDatum(fpoint));
1140 : : }
1141 : :
1142 : 16 : PG_RETURN_DATUM(result);
1143 : : }
1144 : :
1145 : : /*
1146 : : * numeric_cash()
1147 : : * Convert numeric to cash.
1148 : : */
1149 : : Datum
1150 : 8 : numeric_cash(PG_FUNCTION_ARGS)
1151 : : {
1152 : 8 : Numeric amount = PG_GETARG_NUMERIC(0);
1153 : : Cash result;
1154 : : int fpoint;
1155 : : int64 scale;
1156 : : int i;
1157 : : Numeric numeric_scale;
1158 : 8 : struct lconv *lconvert = PGLC_localeconv();
1159 : :
1160 : : /* see comments about frac_digits in cash_in() */
1161 : 8 : fpoint = lconvert->frac_digits;
1162 [ - + - - ]: 8 : if (fpoint < 0 || fpoint > 10)
1163 : 8 : fpoint = 2;
1164 : :
1165 : : /* compute required scale factor */
1166 : 8 : scale = 1;
1167 [ + + ]: 24 : for (i = 0; i < fpoint; i++)
1168 : 16 : scale *= 10;
1169 : :
1170 : : /* multiply the input amount by scale factor */
1171 : 8 : numeric_scale = int64_to_numeric(scale);
1172 : :
1173 : 8 : amount = numeric_mul_safe(amount, numeric_scale, fcinfo->context);
1174 [ - + - - : 8 : if (unlikely(SOFT_ERROR_OCCURRED(fcinfo->context)))
- + - - -
+ ]
1175 : 0 : PG_RETURN_NULL();
1176 : :
1177 : : /* note that numeric_int8 will round to nearest integer for us */
1178 : 8 : result = numeric_int8_safe(amount, fcinfo->context);
1179 [ - + - - : 8 : if (unlikely(SOFT_ERROR_OCCURRED(fcinfo->context)))
- + - - -
+ ]
1180 : 0 : PG_RETURN_NULL();
1181 : :
1182 : 8 : PG_RETURN_CASH(result);
1183 : : }
1184 : :
1185 : : /*
1186 : : * int4_cash()
1187 : : * Convert int4 (int) to cash
1188 : : */
1189 : : Datum
1190 : 28 : int4_cash(PG_FUNCTION_ARGS)
1191 : : {
1192 : 28 : int32 amount = PG_GETARG_INT32(0);
1193 : : Cash result;
1194 : : int fpoint;
1195 : : int64 scale;
1196 : : int i;
1197 : 28 : struct lconv *lconvert = PGLC_localeconv();
1198 : :
1199 : : /* see comments about frac_digits in cash_in() */
1200 : 28 : fpoint = lconvert->frac_digits;
1201 [ - + - - ]: 28 : if (fpoint < 0 || fpoint > 10)
1202 : 28 : fpoint = 2;
1203 : :
1204 : : /* compute required scale factor */
1205 : 28 : scale = 1;
1206 [ + + ]: 84 : for (i = 0; i < fpoint; i++)
1207 : 56 : scale *= 10;
1208 : :
1209 : : /* compute amount * scale, checking for overflow */
1210 [ - + ]: 28 : if (unlikely(pg_mul_s64_overflow(amount, scale, &result)))
1211 [ # # ]: 0 : ereturn(fcinfo->context, (Datum) 0,
1212 : : errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1213 : : errmsg("bigint out of range"));
1214 : :
1215 : 28 : PG_RETURN_CASH(result);
1216 : : }
1217 : :
1218 : : /*
1219 : : * int8_cash()
1220 : : * Convert int8 (bigint) to cash
1221 : : */
1222 : : Datum
1223 : 16 : int8_cash(PG_FUNCTION_ARGS)
1224 : : {
1225 : 16 : int64 amount = PG_GETARG_INT64(0);
1226 : : Cash result;
1227 : : int fpoint;
1228 : : int64 scale;
1229 : : int i;
1230 : 16 : struct lconv *lconvert = PGLC_localeconv();
1231 : :
1232 : : /* see comments about frac_digits in cash_in() */
1233 : 16 : fpoint = lconvert->frac_digits;
1234 [ - + - - ]: 16 : if (fpoint < 0 || fpoint > 10)
1235 : 16 : fpoint = 2;
1236 : :
1237 : : /* compute required scale factor */
1238 : 16 : scale = 1;
1239 [ + + ]: 48 : for (i = 0; i < fpoint; i++)
1240 : 32 : scale *= 10;
1241 : :
1242 : : /* compute amount * scale, checking for overflow */
1243 [ - + ]: 16 : if (unlikely(pg_mul_s64_overflow(amount, scale, &result)))
1244 [ # # ]: 0 : ereturn(fcinfo->context, (Datum) 0,
1245 : : errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1246 : : errmsg("bigint out of range"));
1247 : :
1248 : 16 : PG_RETURN_CASH(result);
1249 : : }
|