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