~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

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

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

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
 *
 
5
 * Functions to allow input and output of money normally but store
 
6
 * and handle it as int4s
 
7
 *
 
8
 * A slightly modified version of this file and a discussion of the
 
9
 * workings can be found in the book "Software Solutions in C" by
 
10
 * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
 
11
 *
 
12
 * $PostgreSQL: pgsql/src/backend/utils/adt/cash.c,v 1.64 2004-08-29 05:06:49 momjian Exp $
 
13
 */
 
14
 
 
15
#include "postgres.h"
 
16
 
 
17
#include <limits.h>
 
18
#include <ctype.h>
 
19
#include <math.h>
 
20
#include <locale.h>
 
21
 
 
22
#include "libpq/pqformat.h"
 
23
#include "miscadmin.h"
 
24
#include "utils/builtins.h"
 
25
#include "utils/cash.h"
 
26
#include "utils/pg_locale.h"
 
27
 
 
28
 
 
29
static const char *num_word(Cash value);
 
30
 
 
31
/* when we go to 64 bit values we will have to modify this */
 
32
#define CASH_BUFSZ              24
 
33
 
 
34
#define TERMINATOR              (CASH_BUFSZ - 1)
 
35
#define LAST_PAREN              (TERMINATOR - 1)
 
36
#define LAST_DIGIT              (LAST_PAREN - 1)
 
37
 
 
38
 
 
39
/*
 
40
 * Cash is a pass-by-ref SQL type, so we must pass and return pointers.
 
41
 * These macros and support routine hide the pass-by-refness.
 
42
 */
 
43
#define PG_GETARG_CASH(n)  (* ((Cash *) PG_GETARG_POINTER(n)))
 
44
#define PG_RETURN_CASH(x)  return CashGetDatum(x)
 
45
 
 
46
static Datum
 
47
CashGetDatum(Cash value)
 
48
{
 
49
        Cash       *result = (Cash *) palloc(sizeof(Cash));
 
50
 
 
51
        *result = value;
 
52
        return PointerGetDatum(result);
 
53
}
 
54
 
 
55
 
 
56
/* cash_in()
 
57
 * Convert a string to a cash data type.
 
58
 * Format is [$]###[,]###[.##]
 
59
 * Examples: 123.45 $123.45 $123,456.78
 
60
 *
 
61
 * This is currently implemented as a 32-bit integer.
 
62
 * XXX HACK It looks as though some of the symbols for
 
63
 *      monetary values returned by localeconv() can be multiple
 
64
 *      bytes/characters. This code assumes one byte only. - tgl 97/04/14
 
65
 * XXX UNHACK Allow the currency symbol to be multibyte.
 
66
 *      - thomas 1998-03-01
 
67
 */
 
68
Datum
 
69
cash_in(PG_FUNCTION_ARGS)
 
70
{
 
71
        char       *str = PG_GETARG_CSTRING(0);
 
72
        Cash            result;
 
73
        Cash            value = 0;
 
74
        Cash            dec = 0;
 
75
        Cash            sgn = 1;
 
76
        int                     seen_dot = 0;
 
77
        const char *s = str;
 
78
        int                     fpoint;
 
79
        char       *csymbol;
 
80
        char            dsymbol,
 
81
                                ssymbol,
 
82
                                psymbol,
 
83
                           *nsymbol;
 
84
 
 
85
        struct lconv *lconvert = PGLC_localeconv();
 
86
 
 
87
        /*
 
88
         * frac_digits will be CHAR_MAX in some locales, notably C.  However,
 
89
         * just testing for == CHAR_MAX is risky, because of compilers like
 
90
         * gcc that "helpfully" let you alter the platform-standard definition
 
91
         * of whether char is signed or not.  If we are so unfortunate as to
 
92
         * get compiled with a nonstandard -fsigned-char or -funsigned-char
 
93
         * switch, then our idea of CHAR_MAX will not agree with libc's. The
 
94
         * safest course is not to test for CHAR_MAX at all, but to impose a
 
95
         * range check for plausible frac_digits values.
 
96
         */
 
97
        fpoint = lconvert->frac_digits;
 
98
        if (fpoint < 0 || fpoint > 10)
 
99
                fpoint = 2;                             /* best guess in this case, I think */
 
100
 
 
101
        dsymbol = ((*lconvert->mon_decimal_point != '\0') ? *lconvert->mon_decimal_point : '.');
 
102
        ssymbol = ((*lconvert->mon_thousands_sep != '\0') ? *lconvert->mon_thousands_sep : ',');
 
103
        csymbol = ((*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$");
 
104
        psymbol = ((*lconvert->positive_sign != '\0') ? *lconvert->positive_sign : '+');
 
105
        nsymbol = ((*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-");
 
106
 
 
107
#ifdef CASHDEBUG
 
108
        printf("cashin- precision '%d'; decimal '%c'; thousands '%c'; currency '%s'; positive '%c'; negative '%s'\n",
 
109
                   fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
 
110
#endif
 
111
 
 
112
        /* we need to add all sorts of checking here.  For now just */
 
113
        /* strip all leading whitespace and any leading currency symbol */
 
114
        while (isspace((unsigned char) *s))
 
115
                s++;
 
116
        if (strncmp(s, csymbol, strlen(csymbol)) == 0)
 
117
                s += strlen(csymbol);
 
118
 
 
119
#ifdef CASHDEBUG
 
120
        printf("cashin- string is '%s'\n", s);
 
121
#endif
 
122
 
 
123
        /* a leading minus or paren signifies a negative number */
 
124
        /* again, better heuristics needed */
 
125
        if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
 
126
        {
 
127
                sgn = -1;
 
128
                s += strlen(nsymbol);
 
129
#ifdef CASHDEBUG
 
130
                printf("cashin- negative symbol; string is '%s'\n", s);
 
131
#endif
 
132
        }
 
133
        else if (*s == '(')
 
134
        {
 
135
                sgn = -1;
 
136
                s++;
 
137
 
 
138
        }
 
139
        else if (*s == psymbol)
 
140
                s++;
 
141
 
 
142
#ifdef CASHDEBUG
 
143
        printf("cashin- string is '%s'\n", s);
 
144
#endif
 
145
 
 
146
        while (isspace((unsigned char) *s))
 
147
                s++;
 
148
        if (strncmp(s, csymbol, strlen(csymbol)) == 0)
 
149
                s += strlen(csymbol);
 
150
 
 
151
#ifdef CASHDEBUG
 
152
        printf("cashin- string is '%s'\n", s);
 
153
#endif
 
154
 
 
155
        for (;; s++)
 
156
        {
 
157
                /* we look for digits as int4 as we have less */
 
158
                /* than the required number of decimal places */
 
159
                if (isdigit((unsigned char) *s) && dec < fpoint)
 
160
                {
 
161
                        value = (value * 10) + *s - '0';
 
162
 
 
163
                        if (seen_dot)
 
164
                                dec++;
 
165
 
 
166
                        /* decimal point? then start counting fractions... */
 
167
                }
 
168
                else if (*s == dsymbol && !seen_dot)
 
169
                {
 
170
                        seen_dot = 1;
 
171
 
 
172
                        /* "thousands" separator? then skip... */
 
173
                }
 
174
                else if (*s == ssymbol)
 
175
                {
 
176
 
 
177
                }
 
178
                else
 
179
                {
 
180
                        /* round off */
 
181
                        if (isdigit((unsigned char) *s) && *s >= '5')
 
182
                                value++;
 
183
 
 
184
                        /* adjust for less than required decimal places */
 
185
                        for (; dec < fpoint; dec++)
 
186
                                value *= 10;
 
187
 
 
188
                        break;
 
189
                }
 
190
        }
 
191
 
 
192
        while (isspace((unsigned char) *s) || *s == '0' || *s == ')')
 
193
                s++;
 
194
 
 
195
        if (*s != '\0')
 
196
                ereport(ERROR,
 
197
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
198
                        errmsg("invalid input syntax for type money: \"%s\"", str)));
 
199
 
 
200
        result = (value * sgn);
 
201
 
 
202
#ifdef CASHDEBUG
 
203
        printf("cashin- result is %d\n", result);
 
204
#endif
 
205
 
 
206
        PG_RETURN_CASH(result);
 
207
}
 
208
 
 
209
 
 
210
/* cash_out()
 
211
 * Function to convert cash to a dollars and cents representation.
 
212
 * XXX HACK This code appears to assume US conventions for
 
213
 *      positive-valued amounts. - tgl 97/04/14
 
214
 */
 
215
Datum
 
216
cash_out(PG_FUNCTION_ARGS)
 
217
{
 
218
        Cash            value = PG_GETARG_CASH(0);
 
219
        char       *result;
 
220
        char            buf[CASH_BUFSZ];
 
221
        int                     minus = 0;
 
222
        int                     count = LAST_DIGIT;
 
223
        int                     point_pos;
 
224
        int                     comma_position = 0;
 
225
        int                     points,
 
226
                                mon_group;
 
227
        char            comma;
 
228
        char       *csymbol,
 
229
                                dsymbol,
 
230
                           *nsymbol;
 
231
        char            convention;
 
232
 
 
233
        struct lconv *lconvert = PGLC_localeconv();
 
234
 
 
235
        /* see comments about frac_digits in cash_in() */
 
236
        points = lconvert->frac_digits;
 
237
        if (points < 0 || points > 10)
 
238
                points = 2;                             /* best guess in this case, I think */
 
239
 
 
240
        /*
 
241
         * As with frac_digits, must apply a range check to mon_grouping to
 
242
         * avoid being fooled by variant CHAR_MAX values.
 
243
         */
 
244
        mon_group = *lconvert->mon_grouping;
 
245
        if (mon_group <= 0 || mon_group > 6)
 
246
                mon_group = 3;
 
247
 
 
248
        comma = ((*lconvert->mon_thousands_sep != '\0') ? *lconvert->mon_thousands_sep : ',');
 
249
        convention = lconvert->n_sign_posn;
 
250
        dsymbol = ((*lconvert->mon_decimal_point != '\0') ? *lconvert->mon_decimal_point : '.');
 
251
        csymbol = ((*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$");
 
252
        nsymbol = ((*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-");
 
253
 
 
254
        point_pos = LAST_DIGIT - points;
 
255
 
 
256
        /* allow more than three decimal points and separate them */
 
257
        if (comma)
 
258
        {
 
259
                point_pos -= (points - 1) / mon_group;
 
260
                comma_position = point_pos % (mon_group + 1);
 
261
        }
 
262
 
 
263
        /* we work with positive amounts and add the minus sign at the end */
 
264
        if (value < 0)
 
265
        {
 
266
                minus = 1;
 
267
                value = -value;
 
268
        }
 
269
 
 
270
        /* allow for trailing negative strings */
 
271
        MemSet(buf, ' ', CASH_BUFSZ);
 
272
        buf[TERMINATOR] = buf[LAST_PAREN] = '\0';
 
273
 
 
274
        while (value || count > (point_pos - 2))
 
275
        {
 
276
                if (points && count == point_pos)
 
277
                        buf[count--] = dsymbol;
 
278
                else if (comma && count % (mon_group + 1) == comma_position)
 
279
                        buf[count--] = comma;
 
280
 
 
281
                buf[count--] = ((unsigned int) value % 10) + '0';
 
282
                value = ((unsigned int) value) / 10;
 
283
        }
 
284
 
 
285
        strncpy((buf + count - strlen(csymbol) + 1), csymbol, strlen(csymbol));
 
286
        count -= strlen(csymbol) - 1;
 
287
 
 
288
        if (buf[LAST_DIGIT] == ',')
 
289
                buf[LAST_DIGIT] = buf[LAST_PAREN];
 
290
 
 
291
        /* see if we need to signify negative amount */
 
292
        if (minus)
 
293
        {
 
294
                if (!PointerIsValid(result = palloc(CASH_BUFSZ + 2 - count + strlen(nsymbol))))
 
295
                        ereport(ERROR,
 
296
                                        (errcode(ERRCODE_OUT_OF_MEMORY),
 
297
                                         errmsg("out of memory")));
 
298
 
 
299
                /* Position code of 0 means use parens */
 
300
                if (convention == 0)
 
301
                        sprintf(result, "(%s)", buf + count);
 
302
                else if (convention == 2)
 
303
                        sprintf(result, "%s%s", buf + count, nsymbol);
 
304
                else
 
305
                        sprintf(result, "%s%s", nsymbol, buf + count);
 
306
        }
 
307
        else
 
308
        {
 
309
                if (!PointerIsValid(result = palloc(CASH_BUFSZ + 2 - count)))
 
310
                        ereport(ERROR,
 
311
                                        (errcode(ERRCODE_OUT_OF_MEMORY),
 
312
                                         errmsg("out of memory")));
 
313
 
 
314
                strcpy(result, buf + count);
 
315
        }
 
316
 
 
317
        PG_RETURN_CSTRING(result);
 
318
}
 
319
 
 
320
/*
 
321
 *              cash_recv                       - converts external binary format to cash
 
322
 */
 
323
Datum
 
324
cash_recv(PG_FUNCTION_ARGS)
 
325
{
 
326
        StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
 
327
 
 
328
        PG_RETURN_CASH((Cash) pq_getmsgint(buf, sizeof(Cash)));
 
329
}
 
330
 
 
331
/*
 
332
 *              cash_send                       - converts cash to binary format
 
333
 */
 
334
Datum
 
335
cash_send(PG_FUNCTION_ARGS)
 
336
{
 
337
        Cash            arg1 = PG_GETARG_CASH(0);
 
338
        StringInfoData buf;
 
339
 
 
340
        pq_begintypsend(&buf);
 
341
        pq_sendint(&buf, arg1, sizeof(Cash));
 
342
        PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 
343
}
 
344
 
 
345
/*
 
346
 * Comparison functions
 
347
 */
 
348
 
 
349
Datum
 
350
cash_eq(PG_FUNCTION_ARGS)
 
351
{
 
352
        Cash            c1 = PG_GETARG_CASH(0);
 
353
        Cash            c2 = PG_GETARG_CASH(1);
 
354
 
 
355
        PG_RETURN_BOOL(c1 == c2);
 
356
}
 
357
 
 
358
Datum
 
359
cash_ne(PG_FUNCTION_ARGS)
 
360
{
 
361
        Cash            c1 = PG_GETARG_CASH(0);
 
362
        Cash            c2 = PG_GETARG_CASH(1);
 
363
 
 
364
        PG_RETURN_BOOL(c1 != c2);
 
365
}
 
366
 
 
367
Datum
 
368
cash_lt(PG_FUNCTION_ARGS)
 
369
{
 
370
        Cash            c1 = PG_GETARG_CASH(0);
 
371
        Cash            c2 = PG_GETARG_CASH(1);
 
372
 
 
373
        PG_RETURN_BOOL(c1 < c2);
 
374
}
 
375
 
 
376
Datum
 
377
cash_le(PG_FUNCTION_ARGS)
 
378
{
 
379
        Cash            c1 = PG_GETARG_CASH(0);
 
380
        Cash            c2 = PG_GETARG_CASH(1);
 
381
 
 
382
        PG_RETURN_BOOL(c1 <= c2);
 
383
}
 
384
 
 
385
Datum
 
386
cash_gt(PG_FUNCTION_ARGS)
 
387
{
 
388
        Cash            c1 = PG_GETARG_CASH(0);
 
389
        Cash            c2 = PG_GETARG_CASH(1);
 
390
 
 
391
        PG_RETURN_BOOL(c1 > c2);
 
392
}
 
393
 
 
394
Datum
 
395
cash_ge(PG_FUNCTION_ARGS)
 
396
{
 
397
        Cash            c1 = PG_GETARG_CASH(0);
 
398
        Cash            c2 = PG_GETARG_CASH(1);
 
399
 
 
400
        PG_RETURN_BOOL(c1 >= c2);
 
401
}
 
402
 
 
403
Datum
 
404
cash_cmp(PG_FUNCTION_ARGS)
 
405
{
 
406
        Cash            c1 = PG_GETARG_CASH(0);
 
407
        Cash            c2 = PG_GETARG_CASH(1);
 
408
 
 
409
        if (c1 > c2)
 
410
                PG_RETURN_INT32(1);
 
411
        else if (c1 == c2)
 
412
                PG_RETURN_INT32(0);
 
413
        else
 
414
                PG_RETURN_INT32(-1);
 
415
}
 
416
 
 
417
 
 
418
/* cash_pl()
 
419
 * Add two cash values.
 
420
 */
 
421
Datum
 
422
cash_pl(PG_FUNCTION_ARGS)
 
423
{
 
424
        Cash            c1 = PG_GETARG_CASH(0);
 
425
        Cash            c2 = PG_GETARG_CASH(1);
 
426
        Cash            result;
 
427
 
 
428
        result = c1 + c2;
 
429
 
 
430
        PG_RETURN_CASH(result);
 
431
}
 
432
 
 
433
 
 
434
/* cash_mi()
 
435
 * Subtract two cash values.
 
436
 */
 
437
Datum
 
438
cash_mi(PG_FUNCTION_ARGS)
 
439
{
 
440
        Cash            c1 = PG_GETARG_CASH(0);
 
441
        Cash            c2 = PG_GETARG_CASH(1);
 
442
        Cash            result;
 
443
 
 
444
        result = c1 - c2;
 
445
 
 
446
        PG_RETURN_CASH(result);
 
447
}
 
448
 
 
449
 
 
450
/* cash_mul_flt8()
 
451
 * Multiply cash by float8.
 
452
 */
 
453
Datum
 
454
cash_mul_flt8(PG_FUNCTION_ARGS)
 
455
{
 
456
        Cash            c = PG_GETARG_CASH(0);
 
457
        float8          f = PG_GETARG_FLOAT8(1);
 
458
        Cash            result;
 
459
 
 
460
        result = c * f;
 
461
        PG_RETURN_CASH(result);
 
462
}
 
463
 
 
464
 
 
465
/* flt8_mul_cash()
 
466
 * Multiply float8 by cash.
 
467
 */
 
468
Datum
 
469
flt8_mul_cash(PG_FUNCTION_ARGS)
 
470
{
 
471
        float8          f = PG_GETARG_FLOAT8(0);
 
472
        Cash            c = PG_GETARG_CASH(1);
 
473
        Cash            result;
 
474
 
 
475
        result = f * c;
 
476
        PG_RETURN_CASH(result);
 
477
}
 
478
 
 
479
 
 
480
/* cash_div_flt8()
 
481
 * Divide cash by float8.
 
482
 *
 
483
 * XXX Don't know if rounding or truncating is correct behavior.
 
484
 * Round for now. - tgl 97/04/15
 
485
 */
 
486
Datum
 
487
cash_div_flt8(PG_FUNCTION_ARGS)
 
488
{
 
489
        Cash            c = PG_GETARG_CASH(0);
 
490
        float8          f = PG_GETARG_FLOAT8(1);
 
491
        Cash            result;
 
492
 
 
493
        if (f == 0.0)
 
494
                ereport(ERROR,
 
495
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
 
496
                                 errmsg("division by zero")));
 
497
 
 
498
        result = rint(c / f);
 
499
        PG_RETURN_CASH(result);
 
500
}
 
501
 
 
502
/* cash_mul_flt4()
 
503
 * Multiply cash by float4.
 
504
 */
 
505
Datum
 
506
cash_mul_flt4(PG_FUNCTION_ARGS)
 
507
{
 
508
        Cash            c = PG_GETARG_CASH(0);
 
509
        float4          f = PG_GETARG_FLOAT4(1);
 
510
        Cash            result;
 
511
 
 
512
        result = c * f;
 
513
        PG_RETURN_CASH(result);
 
514
}
 
515
 
 
516
 
 
517
/* flt4_mul_cash()
 
518
 * Multiply float4 by cash.
 
519
 */
 
520
Datum
 
521
flt4_mul_cash(PG_FUNCTION_ARGS)
 
522
{
 
523
        float4          f = PG_GETARG_FLOAT4(0);
 
524
        Cash            c = PG_GETARG_CASH(1);
 
525
        Cash            result;
 
526
 
 
527
        result = f * c;
 
528
        PG_RETURN_CASH(result);
 
529
}
 
530
 
 
531
 
 
532
/* cash_div_flt4()
 
533
 * Divide cash by float4.
 
534
 *
 
535
 * XXX Don't know if rounding or truncating is correct behavior.
 
536
 * Round for now. - tgl 97/04/15
 
537
 */
 
538
Datum
 
539
cash_div_flt4(PG_FUNCTION_ARGS)
 
540
{
 
541
        Cash            c = PG_GETARG_CASH(0);
 
542
        float4          f = PG_GETARG_FLOAT4(1);
 
543
        Cash            result;
 
544
 
 
545
        if (f == 0.0)
 
546
                ereport(ERROR,
 
547
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
 
548
                                 errmsg("division by zero")));
 
549
 
 
550
        result = rint(c / f);
 
551
        PG_RETURN_CASH(result);
 
552
}
 
553
 
 
554
 
 
555
/* cash_mul_int4()
 
556
 * Multiply cash by int4.
 
557
 */
 
558
Datum
 
559
cash_mul_int4(PG_FUNCTION_ARGS)
 
560
{
 
561
        Cash            c = PG_GETARG_CASH(0);
 
562
        int32           i = PG_GETARG_INT32(1);
 
563
        Cash            result;
 
564
 
 
565
        result = c * i;
 
566
        PG_RETURN_CASH(result);
 
567
}
 
568
 
 
569
 
 
570
/* int4_mul_cash()
 
571
 * Multiply int4 by cash.
 
572
 */
 
573
Datum
 
574
int4_mul_cash(PG_FUNCTION_ARGS)
 
575
{
 
576
        int32           i = PG_GETARG_INT32(0);
 
577
        Cash            c = PG_GETARG_CASH(1);
 
578
        Cash            result;
 
579
 
 
580
        result = i * c;
 
581
        PG_RETURN_CASH(result);
 
582
}
 
583
 
 
584
 
 
585
/* cash_div_int4()
 
586
 * Divide cash by 4-byte integer.
 
587
 *
 
588
 * XXX Don't know if rounding or truncating is correct behavior.
 
589
 * Round for now. - tgl 97/04/15
 
590
 */
 
591
Datum
 
592
cash_div_int4(PG_FUNCTION_ARGS)
 
593
{
 
594
        Cash            c = PG_GETARG_CASH(0);
 
595
        int32           i = PG_GETARG_INT32(1);
 
596
        Cash            result;
 
597
 
 
598
        if (i == 0)
 
599
                ereport(ERROR,
 
600
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
 
601
                                 errmsg("division by zero")));
 
602
 
 
603
        result = rint(c / i);
 
604
 
 
605
        PG_RETURN_CASH(result);
 
606
}
 
607
 
 
608
 
 
609
/* cash_mul_int2()
 
610
 * Multiply cash by int2.
 
611
 */
 
612
Datum
 
613
cash_mul_int2(PG_FUNCTION_ARGS)
 
614
{
 
615
        Cash            c = PG_GETARG_CASH(0);
 
616
        int16           s = PG_GETARG_INT16(1);
 
617
        Cash            result;
 
618
 
 
619
        result = c * s;
 
620
        PG_RETURN_CASH(result);
 
621
}
 
622
 
 
623
/* int2_mul_cash()
 
624
 * Multiply int2 by cash.
 
625
 */
 
626
Datum
 
627
int2_mul_cash(PG_FUNCTION_ARGS)
 
628
{
 
629
        int16           s = PG_GETARG_INT16(0);
 
630
        Cash            c = PG_GETARG_CASH(1);
 
631
        Cash            result;
 
632
 
 
633
        result = s * c;
 
634
        PG_RETURN_CASH(result);
 
635
}
 
636
 
 
637
/* cash_div_int2()
 
638
 * Divide cash by int2.
 
639
 *
 
640
 * XXX Don't know if rounding or truncating is correct behavior.
 
641
 * Round for now. - tgl 97/04/15
 
642
 */
 
643
Datum
 
644
cash_div_int2(PG_FUNCTION_ARGS)
 
645
{
 
646
        Cash            c = PG_GETARG_CASH(0);
 
647
        int16           s = PG_GETARG_INT16(1);
 
648
        Cash            result;
 
649
 
 
650
        if (s == 0)
 
651
                ereport(ERROR,
 
652
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
 
653
                                 errmsg("division by zero")));
 
654
 
 
655
        result = rint(c / s);
 
656
        PG_RETURN_CASH(result);
 
657
}
 
658
 
 
659
/* cashlarger()
 
660
 * Return larger of two cash values.
 
661
 */
 
662
Datum
 
663
cashlarger(PG_FUNCTION_ARGS)
 
664
{
 
665
        Cash            c1 = PG_GETARG_CASH(0);
 
666
        Cash            c2 = PG_GETARG_CASH(1);
 
667
        Cash            result;
 
668
 
 
669
        result = (c1 > c2) ? c1 : c2;
 
670
 
 
671
        PG_RETURN_CASH(result);
 
672
}
 
673
 
 
674
/* cashsmaller()
 
675
 * Return smaller of two cash values.
 
676
 */
 
677
Datum
 
678
cashsmaller(PG_FUNCTION_ARGS)
 
679
{
 
680
        Cash            c1 = PG_GETARG_CASH(0);
 
681
        Cash            c2 = PG_GETARG_CASH(1);
 
682
        Cash            result;
 
683
 
 
684
        result = (c1 < c2) ? c1 : c2;
 
685
 
 
686
        PG_RETURN_CASH(result);
 
687
}
 
688
 
 
689
 
 
690
/* cash_words()
 
691
 * This converts a int4 as well but to a representation using words
 
692
 * Obviously way North American centric - sorry
 
693
 */
 
694
Datum
 
695
cash_words(PG_FUNCTION_ARGS)
 
696
{
 
697
        Cash            value = PG_GETARG_CASH(0);
 
698
        unsigned int val;
 
699
        char            buf[256];
 
700
        char       *p = buf;
 
701
        Cash            m0;
 
702
        Cash            m1;
 
703
        Cash            m2;
 
704
        Cash            m3;
 
705
        text       *result;
 
706
 
 
707
        /* work with positive numbers */
 
708
        if (value < 0)
 
709
        {
 
710
                value = -value;
 
711
                strcpy(buf, "minus ");
 
712
                p += 6;
 
713
        }
 
714
        else
 
715
                buf[0] = '\0';
 
716
 
 
717
        /* Now treat as unsigned, to avoid trouble at INT_MIN */
 
718
        val = (unsigned int) value;
 
719
 
 
720
        m0 = val % 100;                         /* cents */
 
721
        m1 = (val / 100) % 1000;        /* hundreds */
 
722
        m2 = (val / 100000) % 1000; /* thousands */
 
723
        m3 = val / 100000000 % 1000;    /* millions */
 
724
 
 
725
        if (m3)
 
726
        {
 
727
                strcat(buf, num_word(m3));
 
728
                strcat(buf, " million ");
 
729
        }
 
730
 
 
731
        if (m2)
 
732
        {
 
733
                strcat(buf, num_word(m2));
 
734
                strcat(buf, " thousand ");
 
735
        }
 
736
 
 
737
        if (m1)
 
738
                strcat(buf, num_word(m1));
 
739
 
 
740
        if (!*p)
 
741
                strcat(buf, "zero");
 
742
 
 
743
        strcat(buf, (val / 100) == 1 ? " dollar and " : " dollars and ");
 
744
        strcat(buf, num_word(m0));
 
745
        strcat(buf, m0 == 1 ? " cent" : " cents");
 
746
 
 
747
        /* capitalize output */
 
748
        buf[0] = pg_toupper((unsigned char) buf[0]);
 
749
 
 
750
        /* make a text type for output */
 
751
        result = (text *) palloc(strlen(buf) + VARHDRSZ);
 
752
        VARATT_SIZEP(result) = strlen(buf) + VARHDRSZ;
 
753
        memcpy(VARDATA(result), buf, strlen(buf));
 
754
 
 
755
        PG_RETURN_TEXT_P(result);
 
756
}
 
757
 
 
758
 
 
759
/*************************************************************************
 
760
 * Private routines
 
761
 ************************************************************************/
 
762
 
 
763
static const char *
 
764
num_word(Cash value)
 
765
{
 
766
        static char buf[128];
 
767
        static const char *small[] = {
 
768
                "zero", "one", "two", "three", "four", "five", "six", "seven",
 
769
                "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
 
770
                "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
 
771
                "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"
 
772
        };
 
773
        const char **big = small + 18;
 
774
        int                     tu = value % 100;
 
775
 
 
776
        /* deal with the simple cases first */
 
777
        if (value <= 20)
 
778
                return small[value];
 
779
 
 
780
        /* is it an even multiple of 100? */
 
781
        if (!tu)
 
782
        {
 
783
                sprintf(buf, "%s hundred", small[value / 100]);
 
784
                return buf;
 
785
        }
 
786
 
 
787
        /* more than 99? */
 
788
        if (value > 99)
 
789
        {
 
790
                /* is it an even multiple of 10 other than 10? */
 
791
                if (value % 10 == 0 && tu > 10)
 
792
                        sprintf(buf, "%s hundred %s",
 
793
                                        small[value / 100], big[tu / 10]);
 
794
                else if (tu < 20)
 
795
                        sprintf(buf, "%s hundred and %s",
 
796
                                        small[value / 100], small[tu]);
 
797
                else
 
798
                        sprintf(buf, "%s hundred %s %s",
 
799
                                        small[value / 100], big[tu / 10], small[tu % 10]);
 
800
 
 
801
        }
 
802
        else
 
803
        {
 
804
                /* is it an even multiple of 10 other than 10? */
 
805
                if (value % 10 == 0 && tu > 10)
 
806
                        sprintf(buf, "%s", big[tu / 10]);
 
807
                else if (tu < 20)
 
808
                        sprintf(buf, "%s", small[tu]);
 
809
                else
 
810
                        sprintf(buf, "%s %s", big[tu / 10], small[tu % 10]);
 
811
        }
 
812
 
 
813
        return buf;
 
814
}       /* num_word() */