Age Owner 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 *
5940 darcy 38 CBC 11586 : 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 : };
1637 andres 47 11586 : const char *const *big = small + 18;
5940 darcy 48 11586 : int tu = value % 100;
49 :
50 : /* deal with the simple cases first */
51 11586 : if (value <= 20)
52 11319 : return small[value];
53 :
54 : /* is it an even multiple of 100? */
55 267 : if (!tu)
56 : {
5940 darcy 57 UBC 0 : sprintf(buf, "%s hundred", small[value / 100]);
58 0 : return buf;
59 : }
60 :
61 : /* more than 99? */
5940 darcy 62 CBC 267 : if (value > 99)
63 : {
64 : /* is it an even multiple of 10 other than 10? */
65 6 : if (value % 10 == 0 && tu > 10)
5940 darcy 66 UBC 0 : sprintf(buf, "%s hundred %s",
67 0 : small[value / 100], big[tu / 10]);
5940 darcy 68 CBC 6 : else if (tu < 20)
5940 darcy 69 UBC 0 : sprintf(buf, "%s hundred and %s",
70 0 : small[value / 100], small[tu]);
71 : else
5940 darcy 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 261 : if (value % 10 == 0 && tu > 10)
5940 darcy 79 UBC 0 : sprintf(buf, "%s", big[tu / 10]);
5940 darcy 80 CBC 261 : else if (tu < 20)
5940 darcy 81 UBC 0 : sprintf(buf, "%s", small[tu]);
82 : else
5940 darcy 83 CBC 261 : sprintf(buf, "%s %s", big[tu / 10], small[tu % 10]);
84 : }
85 :
86 267 : 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
8284 tgl 96 832 : cash_in(PG_FUNCTION_ARGS)
97 : {
98 832 : char *str = PG_GETARG_CSTRING(0);
116 tgl 99 GNC 832 : Node *escontext = fcinfo->context;
8284 tgl 100 ECB : Cash result;
9344 bruce 101 GIC 832 : Cash value = 0;
9344 bruce 102 CBC 832 : Cash dec = 0;
103 832 : Cash sgn = 1;
4180 tgl 104 832 : bool seen_dot = false;
9344 bruce 105 832 : const char *s = str;
9344 bruce 106 ECB : int fpoint;
107 : char dsymbol;
108 : const char *ssymbol,
109 : *psymbol,
110 : *nsymbol,
111 : *csymbol;
8170 tgl 112 GIC 832 : struct lconv *lconvert = PGLC_localeconv();
8053 bruce 113 ECB :
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 : */
8177 tgl 124 GIC 832 : fpoint = lconvert->frac_digits;
8177 tgl 125 CBC 832 : if (fpoint < 0 || fpoint > 10)
126 832 : fpoint = 2; /* best guess in this case, I think */
9345 bruce 127 ECB :
128 : /* we restrict dsymbol to be a single byte, but not the other symbols */
4180 tgl 129 GIC 832 : if (*lconvert->mon_decimal_point != '\0' &&
4180 tgl 130 LBC 0 : lconvert->mon_decimal_point[1] == '\0')
4180 tgl 131 UBC 0 : dsymbol = *lconvert->mon_decimal_point;
5615 bruce 132 EUB : else
4180 tgl 133 GIC 832 : dsymbol = '.';
4180 tgl 134 CBC 832 : if (*lconvert->mon_thousands_sep != '\0')
4180 tgl 135 LBC 0 : ssymbol = lconvert->mon_thousands_sep;
2118 tgl 136 EUB : else /* ssymbol should not equal dsymbol */
4180 tgl 137 GIC 832 : ssymbol = (dsymbol != ',') ? "," : ".";
4180 tgl 138 CBC 832 : csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
139 832 : psymbol = (*lconvert->positive_sign != '\0') ? lconvert->positive_sign : "+";
140 832 : nsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
9490 scrappy 141 ECB :
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 */
8162 tgl 149 GIC 832 : while (isspace((unsigned char) *s))
8986 bruce 150 LBC 0 : s++;
8986 bruce 151 GBC 832 : if (strncmp(s, csymbol, strlen(csymbol)) == 0)
8986 bruce 152 CBC 62 : s += strlen(csymbol);
4179 tgl 153 832 : while (isspace((unsigned char) *s))
4179 tgl 154 LBC 0 : s++;
9169 lockhart 155 EUB :
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 */
8986 bruce 163 GIC 832 : if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
9169 lockhart 164 ECB : {
9169 lockhart 165 GIC 305 : sgn = -1;
9169 lockhart 166 CBC 305 : s += strlen(nsymbol);
9169 lockhart 167 ECB : }
9169 lockhart 168 GIC 527 : else if (*s == '(')
9345 bruce 169 ECB : {
9345 bruce 170 GIC 6 : sgn = -1;
9345 bruce 171 CBC 6 : s++;
9345 bruce 172 ECB : }
4180 tgl 173 GIC 521 : else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
4180 tgl 174 LBC 0 : s += strlen(psymbol);
9490 scrappy 175 EUB :
176 : #ifdef CASHDEBUG
177 : printf("cashin- string is '%s'\n", s);
178 : #endif
179 :
180 : /* allow whitespace and currency symbol after the sign, too */
8162 tgl 181 GIC 832 : while (isspace((unsigned char) *s))
8986 bruce 182 LBC 0 : s++;
8986 bruce 183 GBC 832 : if (strncmp(s, csymbol, strlen(csymbol)) == 0)
8986 bruce 184 CBC 3 : s += strlen(csymbol);
4179 tgl 185 832 : while (isspace((unsigned char) *s))
4179 tgl 186 LBC 0 : s++;
9169 lockhart 187 EUB :
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 :
4180 tgl 201 GIC 7887 : for (; *s; s++)
9345 bruce 202 ECB : {
203 : /*
204 : * We look for digits as long as we have found less than the required
205 : * number of decimal places.
206 : */
5051 tgl 207 GIC 7097 : if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
9345 bruce 208 CBC 6320 : {
1944 andres 209 6329 : int8 digit = *s - '0';
2404 peter_e 210 ECB :
1944 andres 211 GIC 12652 : if (pg_mul_s64_overflow(value, 10, &value) ||
1944 andres 212 CBC 6323 : pg_sub_s64_overflow(value, digit, &value))
116 tgl 213 GNC 9 : ereturn(escontext, (Datum) 0,
2404 peter_e 214 ECB : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
215 : errmsg("value \"%s\" is out of range for type %s",
216 : str, "money")));
217 :
9345 bruce 218 GIC 6320 : if (seen_dot)
9345 bruce 219 CBC 1455 : dec++;
9345 bruce 220 ECB : }
221 : /* decimal point? then start counting fractions... */
9345 bruce 222 GIC 768 : else if (*s == dsymbol && !seen_dot)
9345 bruce 223 ECB : {
4180 tgl 224 GIC 732 : seen_dot = true;
9345 bruce 225 ECB : }
226 : /* ignore if "thousands" separator, else we're done */
4180 tgl 227 GIC 36 : else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
4180 tgl 228 CBC 3 : s += strlen(ssymbol) - 1;
4180 tgl 229 ECB : else
9345 bruce 230 GIC 33 : break;
9490 scrappy 231 ECB : }
232 :
233 : /* round off if there's another digit */
4180 tgl 234 GIC 823 : if (isdigit((unsigned char) *s) && *s >= '5')
1944 andres 235 ECB : {
236 : /* remember we build the value in the negative */
1944 andres 237 GIC 15 : if (pg_sub_s64_overflow(value, 1, &value))
116 tgl 238 GNC 3 : ereturn(escontext, (Datum) 0,
1944 andres 239 ECB : (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 */
4180 tgl 245 GIC 1002 : for (; dec < fpoint; dec++)
2404 peter_e 246 ECB : {
1944 andres 247 GIC 194 : if (pg_mul_s64_overflow(value, 10, &value))
116 tgl 248 GNC 12 : ereturn(escontext, (Datum) 0,
2404 peter_e 249 ECB : (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 : */
5750 tgl 258 GIC 826 : while (isdigit((unsigned char) *s))
5750 tgl 259 CBC 18 : s++;
4179 tgl 260 ECB :
4180 tgl 261 GIC 814 : while (*s)
4180 tgl 262 ECB : {
4180 tgl 263 GIC 12 : if (isspace((unsigned char) *s) || *s == ')')
4180 tgl 264 CBC 6 : s++;
265 6 : else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
4180 tgl 266 ECB : {
4180 tgl 267 UIC 0 : sgn = -1;
4180 tgl 268 UBC 0 : s += strlen(nsymbol);
4180 tgl 269 EUB : }
4179 tgl 270 GIC 6 : else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
4179 tgl 271 LBC 0 : s += strlen(psymbol);
4179 tgl 272 GBC 6 : else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
4179 tgl 273 LBC 0 : s += strlen(csymbol);
4180 tgl 274 EUB : else
116 tgl 275 GNC 6 : ereturn(escontext, (Datum) 0,
4180 tgl 276 ECB : (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 : */
2404 peter_e 285 GIC 802 : if (sgn > 0)
2404 peter_e 286 ECB : {
1944 andres 287 GIC 503 : if (value == PG_INT64_MIN)
116 tgl 288 GNC 6 : ereturn(escontext, (Datum) 0,
2404 peter_e 289 ECB : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
290 : errmsg("value \"%s\" is out of range for type %s",
291 : str, "money")));
1944 andres 292 GIC 497 : result = -value;
2404 peter_e 293 ECB : }
294 : else
2404 peter_e 295 GIC 299 : result = value;
9496 scrappy 296 ECB :
297 : #ifdef CASHDEBUG
298 : printf("cashin- result is " INT64_FORMAT "\n", result);
299 : #endif
300 :
8284 tgl 301 GIC 796 : PG_RETURN_CASH(result);
8284 tgl 302 ECB : }
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
8284 tgl 310 GIC 613 : cash_out(PG_FUNCTION_ARGS)
9496 scrappy 311 ECB : {
8284 tgl 312 GIC 613 : Cash value = PG_GETARG_CASH(0);
9344 bruce 313 ECB : 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;
8170 tgl 326 GIC 613 : struct lconv *lconvert = PGLC_localeconv();
9319 lockhart 327 ECB :
328 : /* see comments about frac_digits in cash_in() */
8177 tgl 329 GIC 613 : points = lconvert->frac_digits;
8177 tgl 330 CBC 613 : if (points < 0 || points > 10)
331 613 : points = 2; /* best guess in this case, I think */
8177 tgl 332 ECB :
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 : */
9319 lockhart 337 GIC 613 : mon_group = *lconvert->mon_grouping;
8177 tgl 338 CBC 613 : if (mon_group <= 0 || mon_group > 6)
339 613 : mon_group = 3;
8177 tgl 340 ECB :
341 : /* we restrict dsymbol to be a single byte, but not the other symbols */
4180 tgl 342 GIC 613 : if (*lconvert->mon_decimal_point != '\0' &&
4180 tgl 343 LBC 0 : lconvert->mon_decimal_point[1] == '\0')
4180 tgl 344 UBC 0 : dsymbol = *lconvert->mon_decimal_point;
4180 tgl 345 EUB : else
4180 tgl 346 GIC 613 : dsymbol = '.';
4180 tgl 347 CBC 613 : if (*lconvert->mon_thousands_sep != '\0')
4180 tgl 348 LBC 0 : ssymbol = lconvert->mon_thousands_sep;
2118 tgl 349 EUB : else /* ssymbol should not equal dsymbol */
4180 tgl 350 GIC 613 : ssymbol = (dsymbol != ',') ? "," : ".";
4180 tgl 351 CBC 613 : csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
9345 bruce 352 ECB :
9339 bruce 353 GIC 613 : if (value < 0)
9345 bruce 354 ECB : {
355 : /* make the amount positive for digit-reconstruction loop */
7719 tgl 356 GIC 43 : value = -value;
4179 tgl 357 ECB : /* set up formatting data */
4179 tgl 358 GIC 43 : signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
4179 tgl 359 CBC 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;
4179 tgl 362 ECB : }
363 : else
364 : {
4179 tgl 365 GIC 570 : signsymbol = lconvert->positive_sign;
4179 tgl 366 CBC 570 : sign_posn = lconvert->p_sign_posn;
367 570 : cs_precedes = lconvert->p_cs_precedes;
368 570 : sep_by_space = lconvert->p_sep_by_space;
9345 bruce 369 ECB : }
370 :
371 : /* we build the digits+decimal-point+sep string right-to-left in buf[] */
4180 tgl 372 GIC 613 : bufptr = buf + sizeof(buf) - 1;
4180 tgl 373 CBC 613 : *bufptr = '\0';
9345 bruce 374 ECB :
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 : */
4180 tgl 381 GIC 613 : digit_pos = points;
4180 tgl 382 ECB : do
383 : {
4180 tgl 384 GIC 3689 : if (points && digit_pos == 0)
4180 tgl 385 ECB : {
386 : /* insert decimal point, but not if value cannot be fractional */
4180 tgl 387 GIC 613 : *(--bufptr) = dsymbol;
4180 tgl 388 ECB : }
4179 tgl 389 GIC 3076 : else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
4180 tgl 390 ECB : {
391 : /* insert thousands sep, but only to left of radix point */
4180 tgl 392 GIC 513 : bufptr -= strlen(ssymbol);
4180 tgl 393 CBC 513 : memcpy(bufptr, ssymbol, strlen(ssymbol));
4180 tgl 394 ECB : }
395 :
4180 tgl 396 GIC 3689 : *(--bufptr) = ((uint64) value % 10) + '0';
5940 darcy 397 CBC 3689 : value = ((uint64) value) / 10;
4180 tgl 398 3689 : digit_pos--;
399 3689 : } while (value || digit_pos >= 0);
9345 bruce 400 ECB :
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 : */
4179 tgl 425 GIC 613 : switch (sign_posn)
9345 bruce 426 ECB : {
4179 tgl 427 UIC 0 : case 0:
4179 tgl 428 UBC 0 : if (cs_precedes)
3465 peter_e 429 0 : result = psprintf("(%s%s%s)",
3260 bruce 430 EUB : csymbol,
431 : (sep_by_space == 1) ? " " : "",
432 : bufptr);
433 : else
3465 peter_e 434 UIC 0 : result = psprintf("(%s%s%s)",
3260 bruce 435 EUB : bufptr,
436 : (sep_by_space == 1) ? " " : "",
437 : csymbol);
4179 tgl 438 UIC 0 : break;
4179 tgl 439 GBC 613 : case 1:
4179 tgl 440 ECB : default:
4179 tgl 441 GIC 613 : if (cs_precedes)
3465 peter_e 442 CBC 613 : result = psprintf("%s%s%s%s%s",
3260 bruce 443 ECB : signsymbol,
444 : (sep_by_space == 2) ? " " : "",
445 : csymbol,
446 : (sep_by_space == 1) ? " " : "",
447 : bufptr);
448 : else
3465 peter_e 449 UIC 0 : result = psprintf("%s%s%s%s%s",
3260 bruce 450 EUB : signsymbol,
451 : (sep_by_space == 2) ? " " : "",
452 : bufptr,
453 : (sep_by_space == 1) ? " " : "",
454 : csymbol);
4179 tgl 455 GIC 613 : break;
4179 tgl 456 LBC 0 : case 2:
4179 tgl 457 UBC 0 : if (cs_precedes)
3465 peter_e 458 0 : result = psprintf("%s%s%s%s%s",
3260 bruce 459 EUB : csymbol,
460 : (sep_by_space == 1) ? " " : "",
461 : bufptr,
462 : (sep_by_space == 2) ? " " : "",
463 : signsymbol);
464 : else
3465 peter_e 465 UIC 0 : result = psprintf("%s%s%s%s%s",
3260 bruce 466 EUB : bufptr,
467 : (sep_by_space == 1) ? " " : "",
468 : csymbol,
469 : (sep_by_space == 2) ? " " : "",
470 : signsymbol);
4179 tgl 471 UIC 0 : break;
4179 tgl 472 UBC 0 : case 3:
473 0 : if (cs_precedes)
3465 peter_e 474 0 : result = psprintf("%s%s%s%s%s",
3260 bruce 475 EUB : signsymbol,
476 : (sep_by_space == 2) ? " " : "",
477 : csymbol,
478 : (sep_by_space == 1) ? " " : "",
479 : bufptr);
480 : else
3465 peter_e 481 UIC 0 : result = psprintf("%s%s%s%s%s",
3260 bruce 482 EUB : bufptr,
483 : (sep_by_space == 1) ? " " : "",
484 : signsymbol,
485 : (sep_by_space == 2) ? " " : "",
486 : csymbol);
4179 tgl 487 UIC 0 : break;
4179 tgl 488 UBC 0 : case 4:
489 0 : if (cs_precedes)
3465 peter_e 490 0 : result = psprintf("%s%s%s%s%s",
3260 bruce 491 EUB : csymbol,
492 : (sep_by_space == 2) ? " " : "",
493 : signsymbol,
494 : (sep_by_space == 1) ? " " : "",
495 : bufptr);
496 : else
3465 peter_e 497 UIC 0 : result = psprintf("%s%s%s%s%s",
3260 bruce 498 EUB : bufptr,
499 : (sep_by_space == 1) ? " " : "",
500 : csymbol,
501 : (sep_by_space == 2) ? " " : "",
502 : signsymbol);
4179 tgl 503 UIC 0 : break;
9345 bruce 504 EUB : }
505 :
8284 tgl 506 GIC 613 : PG_RETURN_CSTRING(result);
8284 tgl 507 ECB : }
508 :
509 : /*
510 : * cash_recv - converts external binary format to cash
511 : */
512 : Datum
7271 tgl 513 UIC 0 : cash_recv(PG_FUNCTION_ARGS)
7271 tgl 514 EUB : {
7271 tgl 515 UIC 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
7271 tgl 516 EUB :
5710 tgl 517 UIC 0 : PG_RETURN_CASH((Cash) pq_getmsgint64(buf));
7271 tgl 518 EUB : }
519 :
520 : /*
521 : * cash_send - converts cash to binary format
522 : */
523 : Datum
7271 tgl 524 UIC 0 : cash_send(PG_FUNCTION_ARGS)
7271 tgl 525 EUB : {
7271 tgl 526 UIC 0 : Cash arg1 = PG_GETARG_CASH(0);
7271 tgl 527 EUB : StringInfoData buf;
528 :
7271 tgl 529 UIC 0 : pq_begintypsend(&buf);
5710 tgl 530 UBC 0 : pq_sendint64(&buf, arg1);
7271 531 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
7271 tgl 532 EUB : }
533 :
534 : /*
535 : * Comparison functions
536 : */
537 :
538 : Datum
8284 tgl 539 GIC 577 : cash_eq(PG_FUNCTION_ARGS)
9490 scrappy 540 ECB : {
8284 tgl 541 GIC 577 : Cash c1 = PG_GETARG_CASH(0);
8284 tgl 542 CBC 577 : Cash c2 = PG_GETARG_CASH(1);
9496 scrappy 543 ECB :
8284 tgl 544 GIC 577 : PG_RETURN_BOOL(c1 == c2);
8284 tgl 545 ECB : }
546 :
547 : Datum
8284 tgl 548 GIC 33 : cash_ne(PG_FUNCTION_ARGS)
9490 scrappy 549 ECB : {
8284 tgl 550 GIC 33 : Cash c1 = PG_GETARG_CASH(0);
8284 tgl 551 CBC 33 : Cash c2 = PG_GETARG_CASH(1);
9496 scrappy 552 ECB :
8284 tgl 553 GIC 33 : PG_RETURN_BOOL(c1 != c2);
8284 tgl 554 ECB : }
555 :
556 : Datum
8284 tgl 557 GIC 549 : cash_lt(PG_FUNCTION_ARGS)
9496 scrappy 558 ECB : {
8284 tgl 559 GIC 549 : Cash c1 = PG_GETARG_CASH(0);
8284 tgl 560 CBC 549 : Cash c2 = PG_GETARG_CASH(1);
9496 scrappy 561 ECB :
8284 tgl 562 GIC 549 : PG_RETURN_BOOL(c1 < c2);
8284 tgl 563 ECB : }
564 :
565 : Datum
8284 tgl 566 GIC 549 : cash_le(PG_FUNCTION_ARGS)
9496 scrappy 567 ECB : {
8284 tgl 568 GIC 549 : Cash c1 = PG_GETARG_CASH(0);
8284 tgl 569 CBC 549 : Cash c2 = PG_GETARG_CASH(1);
9496 scrappy 570 ECB :
8284 tgl 571 GIC 549 : PG_RETURN_BOOL(c1 <= c2);
8284 tgl 572 ECB : }
573 :
574 : Datum
8284 tgl 575 GIC 549 : cash_gt(PG_FUNCTION_ARGS)
9490 scrappy 576 ECB : {
8284 tgl 577 GIC 549 : Cash c1 = PG_GETARG_CASH(0);
8284 tgl 578 CBC 549 : Cash c2 = PG_GETARG_CASH(1);
9496 scrappy 579 ECB :
8284 tgl 580 GIC 549 : PG_RETURN_BOOL(c1 > c2);
8284 tgl 581 ECB : }
582 :
583 : Datum
8284 tgl 584 GIC 549 : cash_ge(PG_FUNCTION_ARGS)
9496 scrappy 585 ECB : {
8284 tgl 586 GIC 549 : Cash c1 = PG_GETARG_CASH(0);
8284 tgl 587 CBC 549 : Cash c2 = PG_GETARG_CASH(1);
9496 scrappy 588 ECB :
8284 tgl 589 GIC 549 : PG_RETURN_BOOL(c1 >= c2);
8284 tgl 590 ECB : }
591 :
592 : Datum
7175 tgl 593 GIC 731 : cash_cmp(PG_FUNCTION_ARGS)
7175 tgl 594 ECB : {
7175 tgl 595 GIC 731 : Cash c1 = PG_GETARG_CASH(0);
7175 tgl 596 CBC 731 : Cash c2 = PG_GETARG_CASH(1);
7175 tgl 597 ECB :
7175 tgl 598 GIC 731 : if (c1 > c2)
7175 tgl 599 CBC 585 : PG_RETURN_INT32(1);
600 146 : else if (c1 == c2)
601 16 : PG_RETURN_INT32(0);
7175 tgl 602 ECB : else
7175 tgl 603 GIC 130 : PG_RETURN_INT32(-1);
7175 tgl 604 ECB : }
605 :
606 :
607 : /* cash_pl()
608 : * Add two cash values.
609 : */
610 : Datum
8284 tgl 611 GIC 21 : cash_pl(PG_FUNCTION_ARGS)
9490 scrappy 612 ECB : {
8284 tgl 613 GIC 21 : Cash c1 = PG_GETARG_CASH(0);
8284 tgl 614 CBC 21 : Cash c2 = PG_GETARG_CASH(1);
8284 tgl 615 ECB : Cash result;
616 :
8284 tgl 617 GIC 21 : result = c1 + c2;
9490 scrappy 618 ECB :
8284 tgl 619 GIC 21 : PG_RETURN_CASH(result);
8284 tgl 620 ECB : }
621 :
622 :
623 : /* cash_mi()
624 : * Subtract two cash values.
625 : */
626 : Datum
8284 tgl 627 GIC 6 : cash_mi(PG_FUNCTION_ARGS)
9490 scrappy 628 ECB : {
8284 tgl 629 GIC 6 : Cash c1 = PG_GETARG_CASH(0);
8284 tgl 630 CBC 6 : Cash c2 = PG_GETARG_CASH(1);
8284 tgl 631 ECB : Cash result;
632 :
8284 tgl 633 GIC 6 : result = c1 - c2;
9496 scrappy 634 ECB :
8284 tgl 635 GIC 6 : PG_RETURN_CASH(result);
8284 tgl 636 ECB : }
637 :
638 :
639 : /* cash_div_cash()
640 : * Divide cash by cash, returning float8.
641 : */
642 : Datum
4650 tgl 643 GIC 3 : cash_div_cash(PG_FUNCTION_ARGS)
4650 tgl 644 ECB : {
4650 tgl 645 GIC 3 : Cash dividend = PG_GETARG_CASH(0);
4650 tgl 646 CBC 3 : Cash divisor = PG_GETARG_CASH(1);
4650 tgl 647 ECB : float8 quotient;
648 :
4650 tgl 649 GIC 3 : if (divisor == 0)
4650 tgl 650 LBC 0 : ereport(ERROR,
4650 tgl 651 EUB : (errcode(ERRCODE_DIVISION_BY_ZERO),
652 : errmsg("division by zero")));
653 :
4650 tgl 654 GIC 3 : quotient = (float8) dividend / (float8) divisor;
4650 tgl 655 CBC 3 : PG_RETURN_FLOAT8(quotient);
4650 tgl 656 ECB : }
657 :
658 :
659 : /* cash_mul_flt8()
660 : * Multiply cash by float8.
661 : */
662 : Datum
8286 tgl 663 GIC 3 : cash_mul_flt8(PG_FUNCTION_ARGS)
9490 scrappy 664 ECB : {
8286 tgl 665 GIC 3 : Cash c = PG_GETARG_CASH(0);
8286 tgl 666 CBC 3 : float8 f = PG_GETARG_FLOAT8(1);
8286 tgl 667 ECB : Cash result;
668 :
2149 tgl 669 GIC 3 : result = rint(c * f);
8286 tgl 670 CBC 3 : PG_RETURN_CASH(result);
8286 tgl 671 ECB : }
672 :
673 :
674 : /* flt8_mul_cash()
675 : * Multiply float8 by cash.
676 : */
677 : Datum
8286 tgl 678 GIC 3 : flt8_mul_cash(PG_FUNCTION_ARGS)
9332 lockhart 679 ECB : {
8286 tgl 680 GIC 3 : float8 f = PG_GETARG_FLOAT8(0);
8286 tgl 681 CBC 3 : Cash c = PG_GETARG_CASH(1);
8286 tgl 682 ECB : Cash result;
683 :
2149 tgl 684 GIC 3 : result = rint(f * c);
8286 tgl 685 CBC 3 : PG_RETURN_CASH(result);
8286 tgl 686 ECB : }
687 :
688 :
689 : /* cash_div_flt8()
690 : * Divide cash by float8.
691 : */
692 : Datum
8286 tgl 693 GIC 6 : cash_div_flt8(PG_FUNCTION_ARGS)
9490 scrappy 694 ECB : {
8286 tgl 695 GIC 6 : Cash c = PG_GETARG_CASH(0);
8286 tgl 696 CBC 6 : float8 f = PG_GETARG_FLOAT8(1);
8286 tgl 697 ECB : Cash result;
698 :
8286 tgl 699 GIC 6 : if (f == 0.0)
7196 tgl 700 LBC 0 : ereport(ERROR,
7196 tgl 701 EUB : (errcode(ERRCODE_DIVISION_BY_ZERO),
702 : errmsg("division by zero")));
703 :
8286 tgl 704 GIC 6 : result = rint(c / f);
8286 tgl 705 CBC 6 : PG_RETURN_CASH(result);
8286 tgl 706 ECB : }
707 :
708 :
709 : /* cash_mul_flt4()
710 : * Multiply cash by float4.
711 : */
712 : Datum
8286 tgl 713 GIC 3 : cash_mul_flt4(PG_FUNCTION_ARGS)
9332 lockhart 714 ECB : {
8286 tgl 715 GIC 3 : Cash c = PG_GETARG_CASH(0);
8286 tgl 716 CBC 3 : float4 f = PG_GETARG_FLOAT4(1);
8286 tgl 717 ECB : Cash result;
718 :
2149 tgl 719 GIC 3 : result = rint(c * (float8) f);
8286 tgl 720 CBC 3 : PG_RETURN_CASH(result);
8286 tgl 721 ECB : }
722 :
723 :
724 : /* flt4_mul_cash()
725 : * Multiply float4 by cash.
726 : */
727 : Datum
8286 tgl 728 GIC 3 : flt4_mul_cash(PG_FUNCTION_ARGS)
9332 lockhart 729 ECB : {
8286 tgl 730 GIC 3 : float4 f = PG_GETARG_FLOAT4(0);
8286 tgl 731 CBC 3 : Cash c = PG_GETARG_CASH(1);
8286 tgl 732 ECB : Cash result;
733 :
2149 tgl 734 GIC 3 : result = rint((float8) f * c);
8286 tgl 735 CBC 3 : PG_RETURN_CASH(result);
8286 tgl 736 ECB : }
737 :
738 :
739 : /* cash_div_flt4()
740 : * Divide cash by float4.
741 : *
742 : */
743 : Datum
8286 tgl 744 GIC 6 : cash_div_flt4(PG_FUNCTION_ARGS)
9332 lockhart 745 ECB : {
8286 tgl 746 GIC 6 : Cash c = PG_GETARG_CASH(0);
8286 tgl 747 CBC 6 : float4 f = PG_GETARG_FLOAT4(1);
8286 tgl 748 ECB : Cash result;
749 :
8286 tgl 750 GIC 6 : if (f == 0.0)
7196 tgl 751 LBC 0 : ereport(ERROR,
7196 tgl 752 EUB : (errcode(ERRCODE_DIVISION_BY_ZERO),
753 : errmsg("division by zero")));
754 :
2149 tgl 755 GIC 6 : result = rint(c / (float8) f);
8286 tgl 756 CBC 6 : PG_RETURN_CASH(result);
8286 tgl 757 ECB : }
758 :
759 :
760 : /* cash_mul_int8()
761 : * Multiply cash by int8.
762 : */
763 : Datum
5940 darcy 764 GIC 3 : cash_mul_int8(PG_FUNCTION_ARGS)
5940 darcy 765 ECB : {
5940 darcy 766 GIC 3 : Cash c = PG_GETARG_CASH(0);
5940 darcy 767 CBC 3 : int64 i = PG_GETARG_INT64(1);
5940 darcy 768 ECB : Cash result;
769 :
5940 darcy 770 GIC 3 : result = c * i;
5940 darcy 771 CBC 3 : PG_RETURN_CASH(result);
5940 darcy 772 ECB : }
773 :
774 :
775 : /* int8_mul_cash()
776 : * Multiply int8 by cash.
777 : */
778 : Datum
5940 darcy 779 GIC 3 : int8_mul_cash(PG_FUNCTION_ARGS)
5940 darcy 780 ECB : {
5940 darcy 781 GIC 3 : int64 i = PG_GETARG_INT64(0);
5940 darcy 782 CBC 3 : Cash c = PG_GETARG_CASH(1);
5940 darcy 783 ECB : Cash result;
784 :
5940 darcy 785 GIC 3 : result = i * c;
5940 darcy 786 CBC 3 : PG_RETURN_CASH(result);
5940 darcy 787 ECB : }
788 :
789 : /* cash_div_int8()
790 : * Divide cash by 8-byte integer.
791 : */
792 : Datum
5940 darcy 793 GIC 9 : cash_div_int8(PG_FUNCTION_ARGS)
5940 darcy 794 ECB : {
5940 darcy 795 GIC 9 : Cash c = PG_GETARG_CASH(0);
5940 darcy 796 CBC 9 : int64 i = PG_GETARG_INT64(1);
5940 darcy 797 ECB : Cash result;
798 :
5940 darcy 799 GIC 9 : if (i == 0)
5940 darcy 800 LBC 0 : ereport(ERROR,
5940 darcy 801 EUB : (errcode(ERRCODE_DIVISION_BY_ZERO),
802 : errmsg("division by zero")));
803 :
2149 tgl 804 GIC 9 : result = c / i;
5940 darcy 805 ECB :
5940 darcy 806 GIC 9 : PG_RETURN_CASH(result);
5940 darcy 807 ECB : }
808 :
809 :
810 : /* cash_mul_int4()
811 : * Multiply cash by int4.
812 : */
813 : Datum
8335 tgl 814 GIC 3 : cash_mul_int4(PG_FUNCTION_ARGS)
9332 lockhart 815 ECB : {
8335 tgl 816 GIC 3 : Cash c = PG_GETARG_CASH(0);
5710 tgl 817 CBC 3 : int32 i = PG_GETARG_INT32(1);
8335 tgl 818 ECB : Cash result;
819 :
8335 tgl 820 GIC 3 : result = c * i;
8335 tgl 821 CBC 3 : PG_RETURN_CASH(result);
8335 tgl 822 ECB : }
823 :
824 :
825 : /* int4_mul_cash()
826 : * Multiply int4 by cash.
827 : */
828 : Datum
8335 tgl 829 GIC 3 : int4_mul_cash(PG_FUNCTION_ARGS)
9332 lockhart 830 ECB : {
8335 tgl 831 GIC 3 : int32 i = PG_GETARG_INT32(0);
8335 tgl 832 CBC 3 : Cash c = PG_GETARG_CASH(1);
8335 tgl 833 ECB : Cash result;
834 :
8335 tgl 835 GIC 3 : result = i * c;
8335 tgl 836 CBC 3 : PG_RETURN_CASH(result);
8335 tgl 837 ECB : }
838 :
839 :
840 : /* cash_div_int4()
841 : * Divide cash by 4-byte integer.
842 : *
843 : */
844 : Datum
8335 tgl 845 GIC 9 : cash_div_int4(PG_FUNCTION_ARGS)
9332 lockhart 846 ECB : {
8335 tgl 847 GIC 9 : Cash c = PG_GETARG_CASH(0);
5710 tgl 848 CBC 9 : int32 i = PG_GETARG_INT32(1);
8335 tgl 849 ECB : Cash result;
850 :
9332 lockhart 851 GIC 9 : if (i == 0)
7196 tgl 852 LBC 0 : ereport(ERROR,
7196 tgl 853 EUB : (errcode(ERRCODE_DIVISION_BY_ZERO),
854 : errmsg("division by zero")));
855 :
2149 tgl 856 GIC 9 : result = c / i;
9332 lockhart 857 ECB :
8335 tgl 858 GIC 9 : PG_RETURN_CASH(result);
8335 tgl 859 ECB : }
860 :
861 :
862 : /* cash_mul_int2()
863 : * Multiply cash by int2.
864 : */
865 : Datum
8343 tgl 866 GIC 3 : cash_mul_int2(PG_FUNCTION_ARGS)
9332 lockhart 867 ECB : {
8343 tgl 868 GIC 3 : Cash c = PG_GETARG_CASH(0);
8343 tgl 869 CBC 3 : int16 s = PG_GETARG_INT16(1);
8343 tgl 870 ECB : Cash result;
871 :
8343 tgl 872 GIC 3 : result = c * s;
8343 tgl 873 CBC 3 : PG_RETURN_CASH(result);
8343 tgl 874 ECB : }
875 :
876 : /* int2_mul_cash()
877 : * Multiply int2 by cash.
878 : */
879 : Datum
8343 tgl 880 GIC 3 : int2_mul_cash(PG_FUNCTION_ARGS)
9332 lockhart 881 ECB : {
8343 tgl 882 GIC 3 : int16 s = PG_GETARG_INT16(0);
8343 tgl 883 CBC 3 : Cash c = PG_GETARG_CASH(1);
8343 tgl 884 ECB : Cash result;
885 :
8343 tgl 886 GIC 3 : result = s * c;
8343 tgl 887 CBC 3 : PG_RETURN_CASH(result);
8343 tgl 888 ECB : }
889 :
890 : /* cash_div_int2()
891 : * Divide cash by int2.
892 : *
893 : */
894 : Datum
8343 tgl 895 GIC 9 : cash_div_int2(PG_FUNCTION_ARGS)
9332 lockhart 896 ECB : {
8343 tgl 897 GIC 9 : Cash c = PG_GETARG_CASH(0);
8343 tgl 898 CBC 9 : int16 s = PG_GETARG_INT16(1);
8343 tgl 899 ECB : Cash result;
900 :
9332 lockhart 901 GIC 9 : if (s == 0)
7196 tgl 902 LBC 0 : ereport(ERROR,
7196 tgl 903 EUB : (errcode(ERRCODE_DIVISION_BY_ZERO),
904 : errmsg("division by zero")));
905 :
2149 tgl 906 GIC 9 : result = c / s;
8343 tgl 907 CBC 9 : PG_RETURN_CASH(result);
8343 tgl 908 ECB : }
909 :
910 : /* cashlarger()
911 : * Return larger of two cash values.
912 : */
913 : Datum
8284 tgl 914 GIC 3 : cashlarger(PG_FUNCTION_ARGS)
9490 scrappy 915 ECB : {
8284 tgl 916 GIC 3 : Cash c1 = PG_GETARG_CASH(0);
8284 tgl 917 CBC 3 : Cash c2 = PG_GETARG_CASH(1);
8284 tgl 918 ECB : Cash result;
919 :
8284 tgl 920 GIC 3 : result = (c1 > c2) ? c1 : c2;
9490 scrappy 921 ECB :
8284 tgl 922 GIC 3 : PG_RETURN_CASH(result);
8284 tgl 923 ECB : }
924 :
925 : /* cashsmaller()
926 : * Return smaller of two cash values.
927 : */
928 : Datum
8284 tgl 929 GIC 3 : cashsmaller(PG_FUNCTION_ARGS)
9490 scrappy 930 ECB : {
8284 tgl 931 GIC 3 : Cash c1 = PG_GETARG_CASH(0);
8284 tgl 932 CBC 3 : Cash c2 = PG_GETARG_CASH(1);
8284 tgl 933 ECB : Cash result;
934 :
8284 tgl 935 GIC 3 : result = (c1 < c2) ? c1 : c2;
9490 scrappy 936 ECB :
8284 tgl 937 GIC 3 : PG_RETURN_CASH(result);
8284 tgl 938 ECB : }
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
8311 bruce 945 GIC 6006 : cash_words(PG_FUNCTION_ARGS)
9490 scrappy 946 ECB : {
8312 tgl 947 GIC 6006 : Cash value = PG_GETARG_CASH(0);
5624 bruce 948 ECB : uint64 val;
949 : char buf[256];
9344 bruce 950 GIC 6006 : char *p = buf;
9344 bruce 951 ECB : 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 */
8312 tgl 960 GIC 6006 : if (value < 0)
9345 bruce 961 ECB : {
8312 tgl 962 UIC 0 : value = -value;
9345 bruce 963 UBC 0 : strcpy(buf, "minus ");
964 0 : p += 6;
9345 bruce 965 EUB : }
966 : else
8312 tgl 967 GIC 6006 : buf[0] = '\0';
9345 bruce 968 ECB :
969 : /* Now treat as unsigned, to avoid trouble at INT_MIN */
5940 darcy 970 GIC 6006 : val = (uint64) value;
7719 tgl 971 ECB :
5050 bruce 972 GIC 6006 : m0 = val % INT64CONST(100); /* cents */
2118 tgl 973 CBC 6006 : m1 = (val / INT64CONST(100)) % 1000; /* hundreds */
974 6006 : m2 = (val / INT64CONST(100000)) % 1000; /* thousands */
5050 bruce 975 6006 : m3 = (val / INT64CONST(100000000)) % 1000; /* millions */
2118 tgl 976 6006 : m4 = (val / INT64CONST(100000000000)) % 1000; /* billions */
5417 977 6006 : m5 = (val / INT64CONST(100000000000000)) % 1000; /* trillions */
5050 bruce 978 6006 : m6 = (val / INT64CONST(100000000000000000)) % 1000; /* quadrillions */
5940 darcy 979 ECB :
5940 darcy 980 GIC 6006 : if (m6)
5940 darcy 981 ECB : {
5940 darcy 982 UIC 0 : strcat(buf, num_word(m6));
5940 darcy 983 UBC 0 : strcat(buf, " quadrillion ");
5940 darcy 984 EUB : }
985 :
5940 darcy 986 GIC 6006 : if (m5)
5940 darcy 987 ECB : {
5940 darcy 988 UIC 0 : strcat(buf, num_word(m5));
5940 darcy 989 UBC 0 : strcat(buf, " trillion ");
5940 darcy 990 EUB : }
991 :
5940 darcy 992 GIC 6006 : if (m4)
5940 darcy 993 ECB : {
5940 darcy 994 UIC 0 : strcat(buf, num_word(m4));
5940 darcy 995 UBC 0 : strcat(buf, " billion ");
5940 darcy 996 EUB : }
997 :
9345 bruce 998 GIC 6006 : if (m3)
9345 bruce 999 ECB : {
9345 bruce 1000 UIC 0 : strcat(buf, num_word(m3));
9345 bruce 1001 UBC 0 : strcat(buf, " million ");
9345 bruce 1002 EUB : }
1003 :
9345 bruce 1004 GIC 6006 : if (m2)
9345 bruce 1005 ECB : {
9345 bruce 1006 UIC 0 : strcat(buf, num_word(m2));
9345 bruce 1007 UBC 0 : strcat(buf, " thousand ");
9345 bruce 1008 EUB : }
1009 :
9345 bruce 1010 GIC 6006 : if (m1)
9345 bruce 1011 CBC 5580 : strcat(buf, num_word(m1));
9345 bruce 1012 ECB :
9345 bruce 1013 GIC 6006 : if (!*p)
9345 bruce 1014 CBC 426 : strcat(buf, "zero");
9345 bruce 1015 ECB :
7719 tgl 1016 GIC 6006 : strcat(buf, (val / 100) == 1 ? " dollar and " : " dollars and ");
9345 bruce 1017 CBC 6006 : strcat(buf, num_word(m0));
1018 6006 : strcat(buf, m0 == 1 ? " cent" : " cents");
8945 bruce 1019 ECB :
1020 : /* capitalize output */
6911 tgl 1021 GIC 6006 : buf[0] = pg_toupper((unsigned char) buf[0]);
8945 bruce 1022 ECB :
1023 : /* return as text datum */
5493 tgl 1024 GIC 6006 : PG_RETURN_TEXT_P(cstring_to_text(buf));
8312 tgl 1025 ECB : }
1026 :
1027 :
1028 : /* cash_numeric()
1029 : * Convert cash to numeric.
1030 : */
1031 : Datum
4650 tgl 1032 GIC 12 : cash_numeric(PG_FUNCTION_ARGS)
4650 tgl 1033 ECB : {
4650 tgl 1034 GIC 12 : Cash money = PG_GETARG_CASH(0);
1353 tgl 1035 ECB : Datum result;
1036 : int fpoint;
4650 tgl 1037 GIC 12 : struct lconv *lconvert = PGLC_localeconv();
4650 tgl 1038 ECB :
1039 : /* see comments about frac_digits in cash_in() */
4650 tgl 1040 GIC 12 : fpoint = lconvert->frac_digits;
4650 tgl 1041 CBC 12 : if (fpoint < 0 || fpoint > 10)
1042 12 : fpoint = 2;
4650 tgl 1043 ECB :
1044 : /* convert the integral money value to numeric */
942 peter 1045 GIC 12 : result = NumericGetDatum(int64_to_numeric(money));
4650 tgl 1046 ECB :
1047 : /* scale appropriately, if needed */
1353 tgl 1048 GIC 12 : if (fpoint > 0)
1353 tgl 1049 ECB : {
1050 : int64 scale;
1051 : int i;
1052 : Datum numeric_scale;
1053 : Datum quotient;
1054 :
1055 : /* compute required scale factor */
1353 tgl 1056 GIC 12 : scale = 1;
1353 tgl 1057 CBC 36 : for (i = 0; i < fpoint; i++)
1058 24 : scale *= 10;
942 peter 1059 12 : numeric_scale = NumericGetDatum(int64_to_numeric(scale));
1353 tgl 1060 ECB :
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 : */
1353 tgl 1068 GIC 12 : numeric_scale = DirectFunctionCall2(numeric_round,
1353 tgl 1069 ECB : numeric_scale,
1070 : Int32GetDatum(fpoint));
1071 :
1072 : /* Now we can safely divide ... */
1353 tgl 1073 GIC 12 : quotient = DirectFunctionCall2(numeric_div, result, numeric_scale);
4650 tgl 1074 ECB :
1075 : /* ... and forcibly round to exactly the intended number of digits */
1353 tgl 1076 GIC 12 : result = DirectFunctionCall2(numeric_round,
1353 tgl 1077 ECB : quotient,
1078 : Int32GetDatum(fpoint));
1079 : }
1080 :
1353 tgl 1081 GIC 12 : PG_RETURN_DATUM(result);
4650 tgl 1082 ECB : }
1083 :
1084 : /* numeric_cash()
1085 : * Convert numeric to cash.
1086 : */
1087 : Datum
4650 tgl 1088 GIC 6 : numeric_cash(PG_FUNCTION_ARGS)
4650 tgl 1089 ECB : {
4650 tgl 1090 GIC 6 : Datum amount = PG_GETARG_DATUM(0);
4650 tgl 1091 ECB : Cash result;
1092 : int fpoint;
1093 : int64 scale;
1094 : int i;
1095 : Datum numeric_scale;
4650 tgl 1096 GIC 6 : struct lconv *lconvert = PGLC_localeconv();
4650 tgl 1097 ECB :
1098 : /* see comments about frac_digits in cash_in() */
4650 tgl 1099 GIC 6 : fpoint = lconvert->frac_digits;
4650 tgl 1100 CBC 6 : if (fpoint < 0 || fpoint > 10)
1101 6 : fpoint = 2;
4650 tgl 1102 ECB :
1103 : /* compute required scale factor */
4650 tgl 1104 GIC 6 : scale = 1;
4650 tgl 1105 CBC 18 : for (i = 0; i < fpoint; i++)
1106 12 : scale *= 10;
4650 tgl 1107 ECB :
1108 : /* multiply the input amount by scale factor */
942 peter 1109 GIC 6 : numeric_scale = NumericGetDatum(int64_to_numeric(scale));
4650 tgl 1110 CBC 6 : amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);
4650 tgl 1111 ECB :
1112 : /* note that numeric_int8 will round to nearest integer for us */
4650 tgl 1113 GIC 6 : result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));
4650 tgl 1114 ECB :
4650 tgl 1115 GIC 6 : PG_RETURN_CASH(result);
4650 tgl 1116 ECB : }
1117 :
1118 : /* int4_cash()
1119 : * Convert int4 (int) to cash
1120 : */
1121 : Datum
4387 rhaas 1122 GIC 6072 : int4_cash(PG_FUNCTION_ARGS)
4387 rhaas 1123 ECB : {
4382 bruce 1124 GIC 6072 : int32 amount = PG_GETARG_INT32(0);
4382 bruce 1125 ECB : Cash result;
1126 : int fpoint;
1127 : int64 scale;
1128 : int i;
4387 rhaas 1129 GIC 6072 : struct lconv *lconvert = PGLC_localeconv();
4387 rhaas 1130 ECB :
1131 : /* see comments about frac_digits in cash_in() */
4387 rhaas 1132 GIC 6072 : fpoint = lconvert->frac_digits;
4387 rhaas 1133 CBC 6072 : if (fpoint < 0 || fpoint > 10)
1134 6072 : fpoint = 2;
4387 rhaas 1135 ECB :
1136 : /* compute required scale factor */
4387 rhaas 1137 GIC 6072 : scale = 1;
4387 rhaas 1138 CBC 18216 : for (i = 0; i < fpoint; i++)
1139 12144 : scale *= 10;
4387 rhaas 1140 ECB :
1141 : /* compute amount * scale, checking for overflow */
4387 rhaas 1142 GIC 6072 : result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
4382 bruce 1143 ECB : Int64GetDatum(scale)));
1144 :
4387 rhaas 1145 GIC 6072 : PG_RETURN_CASH(result);
4387 rhaas 1146 ECB : }
1147 :
1148 : /* int8_cash()
1149 : * Convert int8 (bigint) to cash
1150 : */
1151 : Datum
4387 rhaas 1152 GIC 12 : int8_cash(PG_FUNCTION_ARGS)
4387 rhaas 1153 ECB : {
4382 bruce 1154 GIC 12 : int64 amount = PG_GETARG_INT64(0);
4382 bruce 1155 ECB : Cash result;
1156 : int fpoint;
1157 : int64 scale;
1158 : int i;
4387 rhaas 1159 GIC 12 : struct lconv *lconvert = PGLC_localeconv();
4387 rhaas 1160 ECB :
1161 : /* see comments about frac_digits in cash_in() */
4387 rhaas 1162 GIC 12 : fpoint = lconvert->frac_digits;
4387 rhaas 1163 CBC 12 : if (fpoint < 0 || fpoint > 10)
1164 12 : fpoint = 2;
4387 rhaas 1165 ECB :
1166 : /* compute required scale factor */
4387 rhaas 1167 GIC 12 : scale = 1;
4387 rhaas 1168 CBC 36 : for (i = 0; i < fpoint; i++)
1169 24 : scale *= 10;
4387 rhaas 1170 ECB :
1171 : /* compute amount * scale, checking for overflow */
4387 rhaas 1172 GIC 12 : result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
4382 bruce 1173 ECB : Int64GetDatum(scale)));
1174 :
4387 rhaas 1175 GIC 12 : PG_RETURN_CASH(result);
4387 rhaas 1176 ECB : }
|