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