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