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