summaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/cash.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/cash.c')
-rw-r--r--src/backend/utils/adt/cash.c757
1 files changed, 0 insertions, 757 deletions
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
deleted file mode 100644
index 4a8ddcf19b6..00000000000
--- a/src/backend/utils/adt/cash.c
+++ /dev/null
@@ -1,757 +0,0 @@
-/*
- * cash.c
- * Written by D'Arcy J.M. Cain
- *
- * Functions to allow input and output of money normally but store
- * and handle it as int4s
- *
- * A slightly modified version of this file and a discussion of the
- * workings can be found in the book "Software Solutions in C" by
- * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
- *
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.53 2002/04/03 05:39:29 petere Exp $
- */
-
-#include "postgres.h"
-
-#include <limits.h>
-#include <ctype.h>
-#include <math.h>
-#include <locale.h>
-
-#include "miscadmin.h"
-#include "utils/builtins.h"
-#include "utils/cash.h"
-#include "utils/pg_locale.h"
-
-
-static const char *num_word(Cash value);
-
-/* when we go to 64 bit values we will have to modify this */
-#define CASH_BUFSZ 24
-
-#define TERMINATOR (CASH_BUFSZ - 1)
-#define LAST_PAREN (TERMINATOR - 1)
-#define LAST_DIGIT (LAST_PAREN - 1)
-
-
-/*
- * Cash is a pass-by-ref SQL type, so we must pass and return pointers.
- * These macros and support routine hide the pass-by-refness.
- */
-#define PG_GETARG_CASH(n) (* ((Cash *) PG_GETARG_POINTER(n)))
-#define PG_RETURN_CASH(x) return CashGetDatum(x)
-
-static Datum
-CashGetDatum(Cash value)
-{
- Cash *result = (Cash *) palloc(sizeof(Cash));
-
- *result = value;
- return PointerGetDatum(result);
-}
-
-
-/* cash_in()
- * Convert a string to a cash data type.
- * Format is [$]###[,]###[.##]
- * Examples: 123.45 $123.45 $123,456.78
- *
- * This is currently implemented as a 32-bit integer.
- * XXX HACK It looks as though some of the symbols for
- * monetary values returned by localeconv() can be multiple
- * bytes/characters. This code assumes one byte only. - tgl 97/04/14
- * XXX UNHACK Allow the currency symbol to be multi-byte.
- * - thomas 1998-03-01
- */
-Datum
-cash_in(PG_FUNCTION_ARGS)
-{
- char *str = PG_GETARG_CSTRING(0);
- Cash result;
- Cash value = 0;
- Cash dec = 0;
- Cash sgn = 1;
- int seen_dot = 0;
- const char *s = str;
- int fpoint;
- char *csymbol;
- char dsymbol,
- ssymbol,
- psymbol,
- *nsymbol;
-
- struct lconv *lconvert = PGLC_localeconv();
-
- /*
- * frac_digits will be CHAR_MAX in some locales, notably C. However,
- * just testing for == CHAR_MAX is risky, because of compilers like
- * gcc that "helpfully" let you alter the platform-standard definition
- * of whether char is signed or not. If we are so unfortunate as to
- * get compiled with a nonstandard -fsigned-char or -funsigned-char
- * switch, then our idea of CHAR_MAX will not agree with libc's. The
- * safest course is not to test for CHAR_MAX at all, but to impose a
- * range check for plausible frac_digits values.
- */
- fpoint = lconvert->frac_digits;
- if (fpoint < 0 || fpoint > 10)
- fpoint = 2; /* best guess in this case, I think */
-
- dsymbol = ((*lconvert->mon_decimal_point != '\0') ? *lconvert->mon_decimal_point : '.');
- ssymbol = ((*lconvert->mon_thousands_sep != '\0') ? *lconvert->mon_thousands_sep : ',');
- csymbol = ((*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$");
- psymbol = ((*lconvert->positive_sign != '\0') ? *lconvert->positive_sign : '+');
- nsymbol = ((*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-");
-
-#ifdef CASHDEBUG
- printf("cashin- precision '%d'; decimal '%c'; thousands '%c'; currency '%s'; positive '%c'; negative '%s'\n",
- fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
-#endif
-
- /* we need to add all sorts of checking here. For now just */
- /* strip all leading whitespace and any leading currency symbol */
- while (isspace((unsigned char) *s))
- s++;
- if (strncmp(s, csymbol, strlen(csymbol)) == 0)
- s += strlen(csymbol);
-
-#ifdef CASHDEBUG
- printf("cashin- string is '%s'\n", s);
-#endif
-
- /* a leading minus or paren signifies a negative number */
- /* again, better heuristics needed */
- if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
- {
- sgn = -1;
- s += strlen(nsymbol);
-#ifdef CASHDEBUG
- printf("cashin- negative symbol; string is '%s'\n", s);
-#endif
- }
- else if (*s == '(')
- {
- sgn = -1;
- s++;
-
- }
- else if (*s == psymbol)
- s++;
-
-#ifdef CASHDEBUG
- printf("cashin- string is '%s'\n", s);
-#endif
-
- while (isspace((unsigned char) *s))
- s++;
- if (strncmp(s, csymbol, strlen(csymbol)) == 0)
- s += strlen(csymbol);
-
-#ifdef CASHDEBUG
- printf("cashin- string is '%s'\n", s);
-#endif
-
- for (;; s++)
- {
- /* we look for digits as int4 as we have less */
- /* than the required number of decimal places */
- if (isdigit((unsigned char) *s) && dec < fpoint)
- {
- value = (value * 10) + *s - '0';
-
- if (seen_dot)
- dec++;
-
- /* decimal point? then start counting fractions... */
- }
- else if (*s == dsymbol && !seen_dot)
- {
- seen_dot = 1;
-
- /* "thousands" separator? then skip... */
- }
- else if (*s == ssymbol)
- {
-
- }
- else
- {
- /* round off */
- if (isdigit((unsigned char) *s) && *s >= '5')
- value++;
-
- /* adjust for less than required decimal places */
- for (; dec < fpoint; dec++)
- value *= 10;
-
- break;
- }
- }
-
- while (isspace((unsigned char) *s) || *s == '0' || *s == ')')
- s++;
-
- if (*s != '\0')
- elog(ERROR, "Bad money external representation %s", str);
-
- result = (value * sgn);
-
-#ifdef CASHDEBUG
- printf("cashin- result is %d\n", result);
-#endif
-
- PG_RETURN_CASH(result);
-}
-
-
-/* cash_out()
- * Function to convert cash to a dollars and cents representation.
- * XXX HACK This code appears to assume US conventions for
- * positive-valued amounts. - tgl 97/04/14
- */
-Datum
-cash_out(PG_FUNCTION_ARGS)
-{
- Cash value = PG_GETARG_CASH(0);
- char *result;
- char buf[CASH_BUFSZ];
- int minus = 0;
- int count = LAST_DIGIT;
- int point_pos;
- int comma_position = 0;
- int points,
- mon_group;
- char comma;
- char *csymbol,
- dsymbol,
- *nsymbol;
- char convention;
-
- struct lconv *lconvert = PGLC_localeconv();
-
- /* see comments about frac_digits in cash_in() */
- points = lconvert->frac_digits;
- if (points < 0 || points > 10)
- points = 2; /* best guess in this case, I think */
-
- /*
- * As with frac_digits, must apply a range check to mon_grouping to
- * avoid being fooled by variant CHAR_MAX values.
- */
- mon_group = *lconvert->mon_grouping;
- if (mon_group <= 0 || mon_group > 6)
- mon_group = 3;
-
- comma = ((*lconvert->mon_thousands_sep != '\0') ? *lconvert->mon_thousands_sep : ',');
- convention = lconvert->n_sign_posn;
- dsymbol = ((*lconvert->mon_decimal_point != '\0') ? *lconvert->mon_decimal_point : '.');
- csymbol = ((*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$");
- nsymbol = ((*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-");
-
- point_pos = LAST_DIGIT - points;
-
- /* allow more than three decimal points and separate them */
- if (comma)
- {
- point_pos -= (points - 1) / mon_group;
- comma_position = point_pos % (mon_group + 1);
- }
-
- /* we work with positive amounts and add the minus sign at the end */
- if (value < 0)
- {
- minus = 1;
- value = -value;
- }
-
- /* allow for trailing negative strings */
- MemSet(buf, ' ', CASH_BUFSZ);
- buf[TERMINATOR] = buf[LAST_PAREN] = '\0';
-
- while (value || count > (point_pos - 2))
- {
- if (points && count == point_pos)
- buf[count--] = dsymbol;
- else if (comma && count % (mon_group + 1) == comma_position)
- buf[count--] = comma;
-
- buf[count--] = ((unsigned int) value % 10) + '0';
- value = ((unsigned int) value) / 10;
- }
-
- strncpy((buf + count - strlen(csymbol) + 1), csymbol, strlen(csymbol));
- count -= strlen(csymbol) - 1;
-
- if (buf[LAST_DIGIT] == ',')
- buf[LAST_DIGIT] = buf[LAST_PAREN];
-
- /* see if we need to signify negative amount */
- if (minus)
- {
- if (!PointerIsValid(result = palloc(CASH_BUFSZ + 2 - count + strlen(nsymbol))))
- elog(ERROR, "Memory allocation failed, can't output cash");
-
- /* Position code of 0 means use parens */
- if (convention == 0)
- sprintf(result, "(%s)", buf + count);
- else if (convention == 2)
- sprintf(result, "%s%s", buf + count, nsymbol);
- else
- sprintf(result, "%s%s", nsymbol, buf + count);
- }
- else
- {
- if (!PointerIsValid(result = palloc(CASH_BUFSZ + 2 - count)))
- elog(ERROR, "Memory allocation failed, can't output cash");
-
- strcpy(result, buf + count);
- }
-
- PG_RETURN_CSTRING(result);
-}
-
-
-Datum
-cash_eq(PG_FUNCTION_ARGS)
-{
- Cash c1 = PG_GETARG_CASH(0);
- Cash c2 = PG_GETARG_CASH(1);
-
- PG_RETURN_BOOL(c1 == c2);
-}
-
-Datum
-cash_ne(PG_FUNCTION_ARGS)
-{
- Cash c1 = PG_GETARG_CASH(0);
- Cash c2 = PG_GETARG_CASH(1);
-
- PG_RETURN_BOOL(c1 != c2);
-}
-
-Datum
-cash_lt(PG_FUNCTION_ARGS)
-{
- Cash c1 = PG_GETARG_CASH(0);
- Cash c2 = PG_GETARG_CASH(1);
-
- PG_RETURN_BOOL(c1 < c2);
-}
-
-Datum
-cash_le(PG_FUNCTION_ARGS)
-{
- Cash c1 = PG_GETARG_CASH(0);
- Cash c2 = PG_GETARG_CASH(1);
-
- PG_RETURN_BOOL(c1 <= c2);
-}
-
-Datum
-cash_gt(PG_FUNCTION_ARGS)
-{
- Cash c1 = PG_GETARG_CASH(0);
- Cash c2 = PG_GETARG_CASH(1);
-
- PG_RETURN_BOOL(c1 > c2);
-}
-
-Datum
-cash_ge(PG_FUNCTION_ARGS)
-{
- Cash c1 = PG_GETARG_CASH(0);
- Cash c2 = PG_GETARG_CASH(1);
-
- PG_RETURN_BOOL(c1 >= c2);
-}
-
-
-/* cash_pl()
- * Add two cash values.
- */
-Datum
-cash_pl(PG_FUNCTION_ARGS)
-{
- Cash c1 = PG_GETARG_CASH(0);
- Cash c2 = PG_GETARG_CASH(1);
- Cash result;
-
- result = c1 + c2;
-
- PG_RETURN_CASH(result);
-}
-
-
-/* cash_mi()
- * Subtract two cash values.
- */
-Datum
-cash_mi(PG_FUNCTION_ARGS)
-{
- Cash c1 = PG_GETARG_CASH(0);
- Cash c2 = PG_GETARG_CASH(1);
- Cash result;
-
- result = c1 - c2;
-
- PG_RETURN_CASH(result);
-}
-
-
-/* cash_mul_flt8()
- * Multiply cash by float8.
- */
-Datum
-cash_mul_flt8(PG_FUNCTION_ARGS)
-{
- Cash c = PG_GETARG_CASH(0);
- float8 f = PG_GETARG_FLOAT8(1);
- Cash result;
-
- result = c * f;
- PG_RETURN_CASH(result);
-}
-
-
-/* flt8_mul_cash()
- * Multiply float8 by cash.
- */
-Datum
-flt8_mul_cash(PG_FUNCTION_ARGS)
-{
- float8 f = PG_GETARG_FLOAT8(0);
- Cash c = PG_GETARG_CASH(1);
- Cash result;
-
- result = f * c;
- PG_RETURN_CASH(result);
-}
-
-
-/* cash_div_flt8()
- * Divide cash by float8.
- *
- * XXX Don't know if rounding or truncating is correct behavior.
- * Round for now. - tgl 97/04/15
- */
-Datum
-cash_div_flt8(PG_FUNCTION_ARGS)
-{
- Cash c = PG_GETARG_CASH(0);
- float8 f = PG_GETARG_FLOAT8(1);
- Cash result;
-
- if (f == 0.0)
- elog(ERROR, "cash_div: divide by 0.0 error");
-
- result = rint(c / f);
- PG_RETURN_CASH(result);
-}
-
-/* cash_mul_flt4()
- * Multiply cash by float4.
- */
-Datum
-cash_mul_flt4(PG_FUNCTION_ARGS)
-{
- Cash c = PG_GETARG_CASH(0);
- float4 f = PG_GETARG_FLOAT4(1);
- Cash result;
-
- result = c * f;
- PG_RETURN_CASH(result);
-}
-
-
-/* flt4_mul_cash()
- * Multiply float4 by cash.
- */
-Datum
-flt4_mul_cash(PG_FUNCTION_ARGS)
-{
- float4 f = PG_GETARG_FLOAT4(0);
- Cash c = PG_GETARG_CASH(1);
- Cash result;
-
- result = f * c;
- PG_RETURN_CASH(result);
-}
-
-
-/* cash_div_flt4()
- * Divide cash by float4.
- *
- * XXX Don't know if rounding or truncating is correct behavior.
- * Round for now. - tgl 97/04/15
- */
-Datum
-cash_div_flt4(PG_FUNCTION_ARGS)
-{
- Cash c = PG_GETARG_CASH(0);
- float4 f = PG_GETARG_FLOAT4(1);
- Cash result;
-
- if (f == 0.0)
- elog(ERROR, "cash_div: divide by 0.0 error");
-
- result = rint(c / f);
- PG_RETURN_CASH(result);
-}
-
-
-/* cash_mul_int4()
- * Multiply cash by int4.
- */
-Datum
-cash_mul_int4(PG_FUNCTION_ARGS)
-{
- Cash c = PG_GETARG_CASH(0);
- int32 i = PG_GETARG_INT32(1);
- Cash result;
-
- result = c * i;
- PG_RETURN_CASH(result);
-}
-
-
-/* int4_mul_cash()
- * Multiply int4 by cash.
- */
-Datum
-int4_mul_cash(PG_FUNCTION_ARGS)
-{
- int32 i = PG_GETARG_INT32(0);
- Cash c = PG_GETARG_CASH(1);
- Cash result;
-
- result = i * c;
- PG_RETURN_CASH(result);
-}
-
-
-/* cash_div_int4()
- * Divide cash by 4-byte integer.
- *
- * XXX Don't know if rounding or truncating is correct behavior.
- * Round for now. - tgl 97/04/15
- */
-Datum
-cash_div_int4(PG_FUNCTION_ARGS)
-{
- Cash c = PG_GETARG_CASH(0);
- int32 i = PG_GETARG_INT32(1);
- Cash result;
-
- if (i == 0)
- elog(ERROR, "cash_div_int4: divide by 0 error");
-
- result = rint(c / i);
-
- PG_RETURN_CASH(result);
-}
-
-
-/* cash_mul_int2()
- * Multiply cash by int2.
- */
-Datum
-cash_mul_int2(PG_FUNCTION_ARGS)
-{
- Cash c = PG_GETARG_CASH(0);
- int16 s = PG_GETARG_INT16(1);
- Cash result;
-
- result = c * s;
- PG_RETURN_CASH(result);
-}
-
-/* int2_mul_cash()
- * Multiply int2 by cash.
- */
-Datum
-int2_mul_cash(PG_FUNCTION_ARGS)
-{
- int16 s = PG_GETARG_INT16(0);
- Cash c = PG_GETARG_CASH(1);
- Cash result;
-
- result = s * c;
- PG_RETURN_CASH(result);
-}
-
-/* cash_div_int2()
- * Divide cash by int2.
- *
- * XXX Don't know if rounding or truncating is correct behavior.
- * Round for now. - tgl 97/04/15
- */
-Datum
-cash_div_int2(PG_FUNCTION_ARGS)
-{
- Cash c = PG_GETARG_CASH(0);
- int16 s = PG_GETARG_INT16(1);
- Cash result;
-
- if (s == 0)
- elog(ERROR, "cash_div: divide by 0 error");
-
- result = rint(c / s);
- PG_RETURN_CASH(result);
-}
-
-/* cashlarger()
- * Return larger of two cash values.
- */
-Datum
-cashlarger(PG_FUNCTION_ARGS)
-{
- Cash c1 = PG_GETARG_CASH(0);
- Cash c2 = PG_GETARG_CASH(1);
- Cash result;
-
- result = (c1 > c2) ? c1 : c2;
-
- PG_RETURN_CASH(result);
-}
-
-/* cashsmaller()
- * Return smaller of two cash values.
- */
-Datum
-cashsmaller(PG_FUNCTION_ARGS)
-{
- Cash c1 = PG_GETARG_CASH(0);
- Cash c2 = PG_GETARG_CASH(1);
- Cash result;
-
- result = (c1 < c2) ? c1 : c2;
-
- PG_RETURN_CASH(result);
-}
-
-
-/* cash_words()
- * This converts a int4 as well but to a representation using words
- * Obviously way North American centric - sorry
- */
-Datum
-cash_words(PG_FUNCTION_ARGS)
-{
- Cash value = PG_GETARG_CASH(0);
- unsigned int val;
- char buf[128];
- char *p = buf;
- Cash m0;
- Cash m1;
- Cash m2;
- Cash m3;
- text *result;
-
- /* work with positive numbers */
- if (value < 0)
- {
- value = -value;
- strcpy(buf, "minus ");
- p += 6;
- }
- else
- buf[0] = '\0';
-
- /* Now treat as unsigned, to avoid trouble at INT_MIN */
- val = (unsigned int) value;
-
- m0 = val % 100; /* cents */
- m1 = (val / 100) % 1000; /* hundreds */
- m2 = (val / 100000) % 1000; /* thousands */
- m3 = val / 100000000 % 1000; /* millions */
-
- if (m3)
- {
- strcat(buf, num_word(m3));
- strcat(buf, " million ");
- }
-
- if (m2)
- {
- strcat(buf, num_word(m2));
- strcat(buf, " thousand ");
- }
-
- if (m1)
- strcat(buf, num_word(m1));
-
- if (!*p)
- strcat(buf, "zero");
-
- strcat(buf, (val / 100) == 1 ? " dollar and " : " dollars and ");
- strcat(buf, num_word(m0));
- strcat(buf, m0 == 1 ? " cent" : " cents");
-
- /* capitalize output */
- buf[0] = toupper((unsigned char) buf[0]);
-
- /* make a text type for output */
- result = (text *) palloc(strlen(buf) + VARHDRSZ);
- VARATT_SIZEP(result) = strlen(buf) + VARHDRSZ;
- memcpy(VARDATA(result), buf, strlen(buf));
-
- PG_RETURN_TEXT_P(result);
-}
-
-
-/*************************************************************************
- * Private routines
- ************************************************************************/
-
-static const char *
-num_word(Cash value)
-{
- static char buf[128];
- static const char *small[] = {
- "zero", "one", "two", "three", "four", "five", "six", "seven",
- "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
- "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
- "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"
- };
- const char **big = small + 18;
- int tu = value % 100;
-
- /* deal with the simple cases first */
- if (value <= 20)
- return small[value];
-
- /* is it an even multiple of 100? */
- if (!tu)
- {
- sprintf(buf, "%s hundred", small[value / 100]);
- return buf;
- }
-
- /* more than 99? */
- if (value > 99)
- {
- /* is it an even multiple of 10 other than 10? */
- if (value % 10 == 0 && tu > 10)
- sprintf(buf, "%s hundred %s",
- small[value / 100], big[tu / 10]);
- else if (tu < 20)
- sprintf(buf, "%s hundred and %s",
- small[value / 100], small[tu]);
- else
- sprintf(buf, "%s hundred %s %s",
- small[value / 100], big[tu / 10], small[tu % 10]);
-
- }
- else
- {
- /* is it an even multiple of 10 other than 10? */
- if (value % 10 == 0 && tu > 10)
- sprintf(buf, "%s", big[tu / 10]);
- else if (tu < 20)
- sprintf(buf, "%s", small[tu]);
- else
- sprintf(buf, "%s %s", big[tu / 10], small[tu % 10]);
- }
-
- return buf;
-} /* num_word() */