YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

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