]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/cash.c
Silence compiler warnings about possibly unset variables.
[postgresql] / src / backend / utils / adt / cash.c
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 = c * f;
625         PG_RETURN_CASH(result);
626 }
627
628
629 /* flt8_mul_cash()
630  * Multiply float8 by cash.
631  */
632 Datum
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 = f * c;
640         PG_RETURN_CASH(result);
641 }
642
643
644 /* cash_div_flt8()
645  * Divide cash by float8.
646  */
647 Datum
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
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 = c * f;
675         PG_RETURN_CASH(result);
676 }
677
678
679 /* flt4_mul_cash()
680  * Multiply float4 by cash.
681  */
682 Datum
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 = f * c;
690         PG_RETURN_CASH(result);
691 }
692
693
694 /* cash_div_flt4()
695  * Divide cash by float4.
696  *
697  */
698 Datum
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 / f);
711         PG_RETURN_CASH(result);
712 }
713
714
715 /* cash_mul_int8()
716  * Multiply cash by int8.
717  */
718 Datum
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
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
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 = rint(c / i);
760
761         PG_RETURN_CASH(result);
762 }
763
764
765 /* cash_mul_int4()
766  * Multiply cash by int4.
767  */
768 Datum
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
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
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 = rint(c / i);
812
813         PG_RETURN_CASH(result);
814 }
815
816
817 /* cash_mul_int2()
818  * Multiply cash by int2.
819  */
820 Datum
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
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
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 = rint(c / s);
862         PG_RETURN_CASH(result);
863 }
864
865 /* cashlarger()
866  * Return larger of two cash values.
867  */
868 Datum
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
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 a int4 as well but to a representation using words
897  * Obviously way North American centric - sorry
898  */
899 Datum
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
987 cash_numeric(PG_FUNCTION_ARGS)
988 {
989         Cash            money = PG_GETARG_CASH(0);
990         Numeric         result;
991         int                     fpoint;
992         int64           scale;
993         int                     i;
994         Datum           amount;
995         Datum           numeric_scale;
996         Datum           quotient;
997         struct lconv *lconvert = PGLC_localeconv();
998
999         /* see comments about frac_digits in cash_in() */
1000         fpoint = lconvert->frac_digits;
1001         if (fpoint < 0 || fpoint > 10)
1002                 fpoint = 2;
1003
1004         /* compute required scale factor */
1005         scale = 1;
1006         for (i = 0; i < fpoint; i++)
1007                 scale *= 10;
1008
1009         /* form the result as money / scale */
1010         amount = DirectFunctionCall1(int8_numeric, Int64GetDatum(money));
1011         numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
1012         quotient = DirectFunctionCall2(numeric_div, amount, numeric_scale);
1013
1014         /* forcibly round to exactly the intended number of digits */
1015         result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
1016                                                                                                  quotient,
1017                                                                                                  Int32GetDatum(fpoint)));
1018
1019         PG_RETURN_NUMERIC(result);
1020 }
1021
1022 /* numeric_cash()
1023  * Convert numeric to cash.
1024  */
1025 Datum
1026 numeric_cash(PG_FUNCTION_ARGS)
1027 {
1028         Datum           amount = PG_GETARG_DATUM(0);
1029         Cash            result;
1030         int                     fpoint;
1031         int64           scale;
1032         int                     i;
1033         Datum           numeric_scale;
1034         struct lconv *lconvert = PGLC_localeconv();
1035
1036         /* see comments about frac_digits in cash_in() */
1037         fpoint = lconvert->frac_digits;
1038         if (fpoint < 0 || fpoint > 10)
1039                 fpoint = 2;
1040
1041         /* compute required scale factor */
1042         scale = 1;
1043         for (i = 0; i < fpoint; i++)
1044                 scale *= 10;
1045
1046         /* multiply the input amount by scale factor */
1047         numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
1048         amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);
1049
1050         /* note that numeric_int8 will round to nearest integer for us */
1051         result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));
1052
1053         PG_RETURN_CASH(result);
1054 }
1055
1056 /* int4_cash()
1057  * Convert int4 (int) to cash
1058  */
1059 Datum
1060 int4_cash(PG_FUNCTION_ARGS)
1061 {
1062         int32           amount = PG_GETARG_INT32(0);
1063         Cash            result;
1064         int                     fpoint;
1065         int64           scale;
1066         int                     i;
1067         struct lconv *lconvert = PGLC_localeconv();
1068
1069         /* see comments about frac_digits in cash_in() */
1070         fpoint = lconvert->frac_digits;
1071         if (fpoint < 0 || fpoint > 10)
1072                 fpoint = 2;
1073
1074         /* compute required scale factor */
1075         scale = 1;
1076         for (i = 0; i < fpoint; i++)
1077                 scale *= 10;
1078
1079         /* compute amount * scale, checking for overflow */
1080         result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
1081                                                                                            Int64GetDatum(scale)));
1082
1083         PG_RETURN_CASH(result);
1084 }
1085
1086 /* int8_cash()
1087  * Convert int8 (bigint) to cash
1088  */
1089 Datum
1090 int8_cash(PG_FUNCTION_ARGS)
1091 {
1092         int64           amount = PG_GETARG_INT64(0);
1093         Cash            result;
1094         int                     fpoint;
1095         int64           scale;
1096         int                     i;
1097         struct lconv *lconvert = PGLC_localeconv();
1098
1099         /* see comments about frac_digits in cash_in() */
1100         fpoint = lconvert->frac_digits;
1101         if (fpoint < 0 || fpoint > 10)
1102                 fpoint = 2;
1103
1104         /* compute required scale factor */
1105         scale = 1;
1106         for (i = 0; i < fpoint; i++)
1107                 scale *= 10;
1108
1109         /* compute amount * scale, checking for overflow */
1110         result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
1111                                                                                            Int64GetDatum(scale)));
1112
1113         PG_RETURN_CASH(result);
1114 }