3 * Written by D'Arcy J.M. Cain
5 * http://www.druid.net/darcy/
7 * Functions to allow input and output of money normally but store
8 * and handle it as 64 bit ints
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.
16 * src/backend/utils/adt/cash.c
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"
34 /*************************************************************************
36 ************************************************************************/
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"
48 const char **big = small + 18;
51 /* deal with the simple cases first */
55 /* is it an even multiple of 100? */
58 sprintf(buf, "%s hundred", small[value / 100]);
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]);
70 sprintf(buf, "%s hundred and %s",
71 small[value / 100], small[tu]);
73 sprintf(buf, "%s hundred %s %s",
74 small[value / 100], big[tu / 10], small[tu % 10]);
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]);
82 sprintf(buf, "%s", small[tu]);
84 sprintf(buf, "%s %s", big[tu / 10], small[tu % 10]);
91 * Convert a string to a cash data type.
92 * Format is [$]###[,]###[.##]
93 * Examples: 123.45 $123.45 $123,456.78
97 cash_in(PG_FUNCTION_ARGS)
99 char *str = PG_GETARG_CSTRING(0);
104 bool seen_dot = false;
112 struct lconv *lconvert = PGLC_localeconv();
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.
124 fpoint = lconvert->frac_digits;
125 if (fpoint < 0 || fpoint > 10)
126 fpoint = 2; /* best guess in this case, I think */
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;
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 : "-";
143 printf("cashin- precision '%d'; decimal '%c'; thousands '%s'; currency '%s'; positive '%s'; negative '%s'\n",
144 fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
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))
151 if (strncmp(s, csymbol, strlen(csymbol)) == 0)
152 s += strlen(csymbol);
153 while (isspace((unsigned char) *s))
157 printf("cashin- string is '%s'\n", s);
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)
166 s += strlen(nsymbol);
173 else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
174 s += strlen(psymbol);
177 printf("cashin- string is '%s'\n", s);
180 /* allow whitespace and currency symbol after the sign, too */
181 while (isspace((unsigned char) *s))
183 if (strncmp(s, csymbol, strlen(csymbol)) == 0)
184 s += strlen(csymbol);
185 while (isspace((unsigned char) *s))
189 printf("cashin- string is '%s'\n", s);
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))
198 value = (value * 10) + (*s - '0');
203 /* decimal point? then start counting fractions... */
204 else if (*s == dsymbol && !seen_dot)
208 /* ignore if "thousands" separator, else we're done */
209 else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
210 s += strlen(ssymbol) - 1;
215 /* round off if there's another digit */
216 if (isdigit((unsigned char) *s) && *s >= '5')
219 /* adjust for less than required decimal places */
220 for (; dec < fpoint; dec++)
224 * should only be trailing digits followed by whitespace, right paren,
225 * trailing sign, and/or trailing currency symbol
227 while (isdigit((unsigned char) *s))
232 if (isspace((unsigned char) *s) || *s == ')')
234 else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
237 s += strlen(nsymbol);
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);
245 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
246 errmsg("invalid input syntax for type money: \"%s\"",
250 result = value * sgn;
253 printf("cashin- result is " INT64_FORMAT "\n", result);
256 PG_RETURN_CASH(result);
261 * Function to convert cash to a dollars and cents representation, using
262 * the lc_monetary locale's formatting.
265 cash_out(PG_FUNCTION_ARGS)
267 Cash value = PG_GETARG_CASH(0);
281 struct lconv *lconvert = PGLC_localeconv();
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 */
289 * As with frac_digits, must apply a range check to mon_grouping to avoid
290 * being fooled by variant CHAR_MAX values.
292 mon_group = *lconvert->mon_grouping;
293 if (mon_group <= 0 || mon_group > 6)
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;
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 : "$";
310 /* make the amount positive for digit-reconstruction loop */
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;
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;
326 /* we build the digits+decimal-point+sep string right-to-left in buf[] */
327 bufptr = buf + sizeof(buf) - 1;
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.
339 if (points && digit_pos == 0)
341 /* insert decimal point, but not if value cannot be fractional */
342 *(--bufptr) = dsymbol;
344 else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
346 /* insert thousands sep, but only to left of radix point */
347 bufptr -= strlen(ssymbol);
348 memcpy(bufptr, ssymbol, strlen(ssymbol));
351 *(--bufptr) = ((uint64) value % 10) + '0';
352 value = ((uint64) value) / 10;
354 } while (value || digit_pos >= 0);
357 * Now, attach currency symbol and sign symbol in the correct order.
359 * The POSIX spec defines these values controlling this code:
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.
368 * p/n_cs_precedes: 0 means currency symbol after value, else before it.
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
380 result = palloc(strlen(bufptr) + strlen(csymbol) + strlen(signsymbol) + 4);
386 sprintf(result, "(%s%s%s)",
388 (sep_by_space == 1) ? " " : "",
391 sprintf(result, "(%s%s%s)",
393 (sep_by_space == 1) ? " " : "",
399 sprintf(result, "%s%s%s%s%s",
401 (sep_by_space == 2) ? " " : "",
403 (sep_by_space == 1) ? " " : "",
406 sprintf(result, "%s%s%s%s%s",
408 (sep_by_space == 2) ? " " : "",
410 (sep_by_space == 1) ? " " : "",
415 sprintf(result, "%s%s%s%s%s",
417 (sep_by_space == 1) ? " " : "",
419 (sep_by_space == 2) ? " " : "",
422 sprintf(result, "%s%s%s%s%s",
424 (sep_by_space == 1) ? " " : "",
426 (sep_by_space == 2) ? " " : "",
431 sprintf(result, "%s%s%s%s%s",
433 (sep_by_space == 2) ? " " : "",
435 (sep_by_space == 1) ? " " : "",
438 sprintf(result, "%s%s%s%s%s",
440 (sep_by_space == 1) ? " " : "",
442 (sep_by_space == 2) ? " " : "",
447 sprintf(result, "%s%s%s%s%s",
449 (sep_by_space == 2) ? " " : "",
451 (sep_by_space == 1) ? " " : "",
454 sprintf(result, "%s%s%s%s%s",
456 (sep_by_space == 1) ? " " : "",
458 (sep_by_space == 2) ? " " : "",
463 PG_RETURN_CSTRING(result);
467 * cash_recv - converts external binary format to cash
470 cash_recv(PG_FUNCTION_ARGS)
472 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
474 PG_RETURN_CASH((Cash) pq_getmsgint64(buf));
478 * cash_send - converts cash to binary format
481 cash_send(PG_FUNCTION_ARGS)
483 Cash arg1 = PG_GETARG_CASH(0);
486 pq_begintypsend(&buf);
487 pq_sendint64(&buf, arg1);
488 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
492 * Comparison functions
496 cash_eq(PG_FUNCTION_ARGS)
498 Cash c1 = PG_GETARG_CASH(0);
499 Cash c2 = PG_GETARG_CASH(1);
501 PG_RETURN_BOOL(c1 == c2);
505 cash_ne(PG_FUNCTION_ARGS)
507 Cash c1 = PG_GETARG_CASH(0);
508 Cash c2 = PG_GETARG_CASH(1);
510 PG_RETURN_BOOL(c1 != c2);
514 cash_lt(PG_FUNCTION_ARGS)
516 Cash c1 = PG_GETARG_CASH(0);
517 Cash c2 = PG_GETARG_CASH(1);
519 PG_RETURN_BOOL(c1 < c2);
523 cash_le(PG_FUNCTION_ARGS)
525 Cash c1 = PG_GETARG_CASH(0);
526 Cash c2 = PG_GETARG_CASH(1);
528 PG_RETURN_BOOL(c1 <= c2);
532 cash_gt(PG_FUNCTION_ARGS)
534 Cash c1 = PG_GETARG_CASH(0);
535 Cash c2 = PG_GETARG_CASH(1);
537 PG_RETURN_BOOL(c1 > c2);
541 cash_ge(PG_FUNCTION_ARGS)
543 Cash c1 = PG_GETARG_CASH(0);
544 Cash c2 = PG_GETARG_CASH(1);
546 PG_RETURN_BOOL(c1 >= c2);
550 cash_cmp(PG_FUNCTION_ARGS)
552 Cash c1 = PG_GETARG_CASH(0);
553 Cash c2 = PG_GETARG_CASH(1);
565 * Add two cash values.
568 cash_pl(PG_FUNCTION_ARGS)
570 Cash c1 = PG_GETARG_CASH(0);
571 Cash c2 = PG_GETARG_CASH(1);
576 PG_RETURN_CASH(result);
581 * Subtract two cash values.
584 cash_mi(PG_FUNCTION_ARGS)
586 Cash c1 = PG_GETARG_CASH(0);
587 Cash c2 = PG_GETARG_CASH(1);
592 PG_RETURN_CASH(result);
597 * Divide cash by cash, returning float8.
600 cash_div_cash(PG_FUNCTION_ARGS)
602 Cash dividend = PG_GETARG_CASH(0);
603 Cash divisor = PG_GETARG_CASH(1);
608 (errcode(ERRCODE_DIVISION_BY_ZERO),
609 errmsg("division by zero")));
611 quotient = (float8) dividend / (float8) divisor;
612 PG_RETURN_FLOAT8(quotient);
617 * Multiply cash by float8.
620 cash_mul_flt8(PG_FUNCTION_ARGS)
622 Cash c = PG_GETARG_CASH(0);
623 float8 f = PG_GETARG_FLOAT8(1);
627 PG_RETURN_CASH(result);
632 * Multiply float8 by cash.
635 flt8_mul_cash(PG_FUNCTION_ARGS)
637 float8 f = PG_GETARG_FLOAT8(0);
638 Cash c = PG_GETARG_CASH(1);
642 PG_RETURN_CASH(result);
647 * Divide cash by float8.
650 cash_div_flt8(PG_FUNCTION_ARGS)
652 Cash c = PG_GETARG_CASH(0);
653 float8 f = PG_GETARG_FLOAT8(1);
658 (errcode(ERRCODE_DIVISION_BY_ZERO),
659 errmsg("division by zero")));
661 result = rint(c / f);
662 PG_RETURN_CASH(result);
667 * Multiply cash by float4.
670 cash_mul_flt4(PG_FUNCTION_ARGS)
672 Cash c = PG_GETARG_CASH(0);
673 float4 f = PG_GETARG_FLOAT4(1);
677 PG_RETURN_CASH(result);
682 * Multiply float4 by cash.
685 flt4_mul_cash(PG_FUNCTION_ARGS)
687 float4 f = PG_GETARG_FLOAT4(0);
688 Cash c = PG_GETARG_CASH(1);
692 PG_RETURN_CASH(result);
697 * Divide cash by float4.
701 cash_div_flt4(PG_FUNCTION_ARGS)
703 Cash c = PG_GETARG_CASH(0);
704 float4 f = PG_GETARG_FLOAT4(1);
709 (errcode(ERRCODE_DIVISION_BY_ZERO),
710 errmsg("division by zero")));
712 result = rint(c / f);
713 PG_RETURN_CASH(result);
718 * Multiply cash by int8.
721 cash_mul_int8(PG_FUNCTION_ARGS)
723 Cash c = PG_GETARG_CASH(0);
724 int64 i = PG_GETARG_INT64(1);
728 PG_RETURN_CASH(result);
733 * Multiply int8 by cash.
736 int8_mul_cash(PG_FUNCTION_ARGS)
738 int64 i = PG_GETARG_INT64(0);
739 Cash c = PG_GETARG_CASH(1);
743 PG_RETURN_CASH(result);
747 * Divide cash by 8-byte integer.
750 cash_div_int8(PG_FUNCTION_ARGS)
752 Cash c = PG_GETARG_CASH(0);
753 int64 i = PG_GETARG_INT64(1);
758 (errcode(ERRCODE_DIVISION_BY_ZERO),
759 errmsg("division by zero")));
761 result = rint(c / i);
763 PG_RETURN_CASH(result);
768 * Multiply cash by int4.
771 cash_mul_int4(PG_FUNCTION_ARGS)
773 Cash c = PG_GETARG_CASH(0);
774 int32 i = PG_GETARG_INT32(1);
778 PG_RETURN_CASH(result);
783 * Multiply int4 by cash.
786 int4_mul_cash(PG_FUNCTION_ARGS)
788 int32 i = PG_GETARG_INT32(0);
789 Cash c = PG_GETARG_CASH(1);
793 PG_RETURN_CASH(result);
798 * Divide cash by 4-byte integer.
802 cash_div_int4(PG_FUNCTION_ARGS)
804 Cash c = PG_GETARG_CASH(0);
805 int32 i = PG_GETARG_INT32(1);
810 (errcode(ERRCODE_DIVISION_BY_ZERO),
811 errmsg("division by zero")));
813 result = rint(c / i);
815 PG_RETURN_CASH(result);
820 * Multiply cash by int2.
823 cash_mul_int2(PG_FUNCTION_ARGS)
825 Cash c = PG_GETARG_CASH(0);
826 int16 s = PG_GETARG_INT16(1);
830 PG_RETURN_CASH(result);
834 * Multiply int2 by cash.
837 int2_mul_cash(PG_FUNCTION_ARGS)
839 int16 s = PG_GETARG_INT16(0);
840 Cash c = PG_GETARG_CASH(1);
844 PG_RETURN_CASH(result);
848 * Divide cash by int2.
852 cash_div_int2(PG_FUNCTION_ARGS)
854 Cash c = PG_GETARG_CASH(0);
855 int16 s = PG_GETARG_INT16(1);
860 (errcode(ERRCODE_DIVISION_BY_ZERO),
861 errmsg("division by zero")));
863 result = rint(c / s);
864 PG_RETURN_CASH(result);
868 * Return larger of two cash values.
871 cashlarger(PG_FUNCTION_ARGS)
873 Cash c1 = PG_GETARG_CASH(0);
874 Cash c2 = PG_GETARG_CASH(1);
877 result = (c1 > c2) ? c1 : c2;
879 PG_RETURN_CASH(result);
883 * Return smaller of two cash values.
886 cashsmaller(PG_FUNCTION_ARGS)
888 Cash c1 = PG_GETARG_CASH(0);
889 Cash c2 = PG_GETARG_CASH(1);
892 result = (c1 < c2) ? c1 : c2;
894 PG_RETURN_CASH(result);
898 * This converts a int4 as well but to a representation using words
899 * Obviously way North American centric - sorry
902 cash_words(PG_FUNCTION_ARGS)
904 Cash value = PG_GETARG_CASH(0);
916 /* work with positive numbers */
920 strcpy(buf, "minus ");
926 /* Now treat as unsigned, to avoid trouble at INT_MIN */
927 val = (uint64) value;
929 m0 = val % INT64CONST(100); /* cents */
930 m1 = (val / INT64CONST(100)) % 1000; /* hundreds */
931 m2 = (val / INT64CONST(100000)) % 1000; /* thousands */
932 m3 = (val / INT64CONST(100000000)) % 1000; /* millions */
933 m4 = (val / INT64CONST(100000000000)) % 1000; /* billions */
934 m5 = (val / INT64CONST(100000000000000)) % 1000; /* trillions */
935 m6 = (val / INT64CONST(100000000000000000)) % 1000; /* quadrillions */
939 strcat(buf, num_word(m6));
940 strcat(buf, " quadrillion ");
945 strcat(buf, num_word(m5));
946 strcat(buf, " trillion ");
951 strcat(buf, num_word(m4));
952 strcat(buf, " billion ");
957 strcat(buf, num_word(m3));
958 strcat(buf, " million ");
963 strcat(buf, num_word(m2));
964 strcat(buf, " thousand ");
968 strcat(buf, num_word(m1));
973 strcat(buf, (val / 100) == 1 ? " dollar and " : " dollars and ");
974 strcat(buf, num_word(m0));
975 strcat(buf, m0 == 1 ? " cent" : " cents");
977 /* capitalize output */
978 buf[0] = pg_toupper((unsigned char) buf[0]);
980 /* return as text datum */
981 PG_RETURN_TEXT_P(cstring_to_text(buf));
986 * Convert cash to numeric.
989 cash_numeric(PG_FUNCTION_ARGS)
991 Cash money = PG_GETARG_CASH(0);
999 struct lconv *lconvert = PGLC_localeconv();
1001 /* see comments about frac_digits in cash_in() */
1002 fpoint = lconvert->frac_digits;
1003 if (fpoint < 0 || fpoint > 10)
1006 /* compute required scale factor */
1008 for (i = 0; i < fpoint; i++)
1011 /* form the result as money / scale */
1012 amount = DirectFunctionCall1(int8_numeric, Int64GetDatum(money));
1013 numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
1014 quotient = DirectFunctionCall2(numeric_div, amount, numeric_scale);
1016 /* forcibly round to exactly the intended number of digits */
1017 result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
1019 Int32GetDatum(fpoint)));
1021 PG_RETURN_NUMERIC(result);
1025 * Convert numeric to cash.
1028 numeric_cash(PG_FUNCTION_ARGS)
1030 Datum amount = PG_GETARG_DATUM(0);
1035 Datum numeric_scale;
1036 struct lconv *lconvert = PGLC_localeconv();
1038 /* see comments about frac_digits in cash_in() */
1039 fpoint = lconvert->frac_digits;
1040 if (fpoint < 0 || fpoint > 10)
1043 /* compute required scale factor */
1045 for (i = 0; i < fpoint; i++)
1048 /* multiply the input amount by scale factor */
1049 numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
1050 amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);
1052 /* note that numeric_int8 will round to nearest integer for us */
1053 result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));
1055 PG_RETURN_CASH(result);
1059 * Convert int4 (int) to cash
1062 int4_cash(PG_FUNCTION_ARGS)
1064 int32 amount = PG_GETARG_INT32(0);
1069 struct lconv *lconvert = PGLC_localeconv();
1071 /* see comments about frac_digits in cash_in() */
1072 fpoint = lconvert->frac_digits;
1073 if (fpoint < 0 || fpoint > 10)
1076 /* compute required scale factor */
1078 for (i = 0; i < fpoint; i++)
1081 /* compute amount * scale, checking for overflow */
1082 result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
1083 Int64GetDatum(scale)));
1085 PG_RETURN_CASH(result);
1089 * Convert int8 (bigint) to cash
1092 int8_cash(PG_FUNCTION_ARGS)
1094 int64 amount = PG_GETARG_INT64(0);
1099 struct lconv *lconvert = PGLC_localeconv();
1101 /* see comments about frac_digits in cash_in() */
1102 fpoint = lconvert->frac_digits;
1103 if (fpoint < 0 || fpoint > 10)
1106 /* compute required scale factor */
1108 for (i = 0; i < fpoint; i++)
1111 /* compute amount * scale, checking for overflow */
1112 result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
1113 Int64GetDatum(scale)));
1115 PG_RETURN_CASH(result);