~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-security

« back to all changes in this revision

Viewing changes to src/backend/utils/adt/cash.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

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