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