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