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