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