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