]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/cash.c
Add support for EUI-64 MAC addresses as macaddr8
[postgresql] / src / backend / utils / adt / cash.c
index fde02ab3177a3351dd92dfa6514c8ce6325e50fd..5afadb65d115606605607a08de48eb1930d72685 100644 (file)
@@ -21,7 +21,6 @@
 #include <limits.h>
 #include <ctype.h>
 #include <math.h>
-#include <locale.h>
 
 #include "libpq/pqformat.h"
 #include "utils/builtins.h"
@@ -133,7 +132,7 @@ cash_in(PG_FUNCTION_ARGS)
                dsymbol = '.';
        if (*lconvert->mon_thousands_sep != '\0')
                ssymbol = lconvert->mon_thousands_sep;
-       else                                            /* ssymbol should not equal dsymbol */
+       else    /* ssymbol should not equal dsymbol */
                ssymbol = (dsymbol != ',') ? "," : ".";
        csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
        psymbol = (*lconvert->positive_sign != '\0') ? lconvert->positive_sign : "+";
@@ -150,6 +149,8 @@ cash_in(PG_FUNCTION_ARGS)
                s++;
        if (strncmp(s, csymbol, strlen(csymbol)) == 0)
                s += strlen(csymbol);
+       while (isspace((unsigned char) *s))
+               s++;
 
 #ifdef CASHDEBUG
        printf("cashin- string is '%s'\n", s);
@@ -180,18 +181,37 @@ cash_in(PG_FUNCTION_ARGS)
                s++;
        if (strncmp(s, csymbol, strlen(csymbol)) == 0)
                s += strlen(csymbol);
+       while (isspace((unsigned char) *s))
+               s++;
 
 #ifdef CASHDEBUG
        printf("cashin- string is '%s'\n", s);
 #endif
 
+       /*
+        * We accumulate the absolute amount in "value" and then apply the sign at
+        * the end.  (The sign can appear before or after the digits, so it would
+        * be more complicated to do otherwise.)  Because of the larger range of
+        * negative signed integers, we build "value" in the negative and then
+        * flip the sign at the end, catching most-negative-number overflow if
+        * necessary.
+        */
+
        for (; *s; s++)
        {
                /* we look for digits as long as we have found less */
                /* than the required number of decimal places */
                if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
                {
-                       value = (value * 10) + (*s - '0');
+                       Cash newvalue = (value * 10) - (*s - '0');
+
+                       if (newvalue / 10 != value)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                                errmsg("value \"%s\" is out of range for type %s",
+                                                               str, "money")));
+
+                       value = newvalue;
 
                        if (seen_dot)
                                dec++;
@@ -210,18 +230,35 @@ cash_in(PG_FUNCTION_ARGS)
 
        /* round off if there's another digit */
        if (isdigit((unsigned char) *s) && *s >= '5')
-               value++;
+               value--;  /* remember we build the value in the negative */
+
+       if (value > 0)
+               ereport(ERROR,
+                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                errmsg("value \"%s\" is out of range for type %s",
+                                               str, "money")));
 
        /* adjust for less than required decimal places */
        for (; dec < fpoint; dec++)
-               value *= 10;
+       {
+               Cash newvalue = value * 10;
+
+               if (newvalue / 10 != value)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                        errmsg("value \"%s\" is out of range for type %s",
+                                                       str, "money")));
+
+               value = newvalue;
+       }
 
        /*
         * should only be trailing digits followed by whitespace, right paren,
-        * or possibly a trailing minus sign
+        * trailing sign, and/or trailing currency symbol
         */
        while (isdigit((unsigned char) *s))
                s++;
+
        while (*s)
        {
                if (isspace((unsigned char) *s) || *s == ')')
@@ -231,14 +268,30 @@ cash_in(PG_FUNCTION_ARGS)
                        sgn = -1;
                        s += strlen(nsymbol);
                }
+               else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
+                       s += strlen(psymbol);
+               else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
+                       s += strlen(csymbol);
                else
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("invalid input syntax for type money: \"%s\"",
-                                                       str)));
+                                        errmsg("invalid input syntax for type %s: \"%s\"",
+                                                       "money", str)));
        }
 
-       result = value * sgn;
+       /* If the value is supposed to be positive, flip the sign, but check for
+        * the most negative number. */
+       if (sgn > 0)
+       {
+               result = -value;
+               if (result < 0)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                        errmsg("value \"%s\" is out of range for type %s",
+                                                       str, "money")));
+       }
+       else
+               result = value;
 
 #ifdef CASHDEBUG
        printf("cashin- result is " INT64_FORMAT "\n", result);
@@ -259,15 +312,16 @@ cash_out(PG_FUNCTION_ARGS)
        char       *result;
        char            buf[128];
        char       *bufptr;
-       bool            minus = false;
        int                     digit_pos;
        int                     points,
                                mon_group;
        char            dsymbol;
        const char *ssymbol,
                           *csymbol,
-                          *nsymbol;
-       char            convention;
+                          *signsymbol;
+       char            sign_posn,
+                               cs_precedes,
+                               sep_by_space;
        struct lconv *lconvert = PGLC_localeconv();
 
        /* see comments about frac_digits in cash_in() */
@@ -283,8 +337,6 @@ cash_out(PG_FUNCTION_ARGS)
        if (mon_group <= 0 || mon_group > 6)
                mon_group = 3;
 
-       convention = lconvert->n_sign_posn;
-
        /* we restrict dsymbol to be a single byte, but not the other symbols */
        if (*lconvert->mon_decimal_point != '\0' &&
                lconvert->mon_decimal_point[1] == '\0')
@@ -293,19 +345,29 @@ cash_out(PG_FUNCTION_ARGS)
                dsymbol = '.';
        if (*lconvert->mon_thousands_sep != '\0')
                ssymbol = lconvert->mon_thousands_sep;
-       else                                            /* ssymbol should not equal dsymbol */
+       else    /* ssymbol should not equal dsymbol */
                ssymbol = (dsymbol != ',') ? "," : ".";
        csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
-       nsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
 
-       /* we work with positive amounts and add the minus sign at the end */
        if (value < 0)
        {
-               minus = true;
+               /* make the amount positive for digit-reconstruction loop */
                value = -value;
+               /* set up formatting data */
+               signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
+               sign_posn = lconvert->n_sign_posn;
+               cs_precedes = lconvert->n_cs_precedes;
+               sep_by_space = lconvert->n_sep_by_space;
+       }
+       else
+       {
+               signsymbol = lconvert->positive_sign;
+               sign_posn = lconvert->p_sign_posn;
+               cs_precedes = lconvert->p_cs_precedes;
+               sep_by_space = lconvert->p_sep_by_space;
        }
 
-       /* we build the result string right-to-left in buf[] */
+       /* we build the digits+decimal-point+sep string right-to-left in buf[] */
        bufptr = buf + sizeof(buf) - 1;
        *bufptr = '\0';
 
@@ -320,12 +382,12 @@ cash_out(PG_FUNCTION_ARGS)
        {
                if (points && digit_pos == 0)
                {
-                       /* insert decimal point */
+                       /* insert decimal point, but not if value cannot be fractional */
                        *(--bufptr) = dsymbol;
                }
-               else if (digit_pos < points && (digit_pos % mon_group) == 0)
+               else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
                {
-                       /* insert thousands sep */
+                       /* insert thousands sep, but only to left of radix point */
                        bufptr -= strlen(ssymbol);
                        memcpy(bufptr, ssymbol, strlen(ssymbol));
                }
@@ -335,27 +397,109 @@ cash_out(PG_FUNCTION_ARGS)
                digit_pos--;
        } while (value || digit_pos >= 0);
 
-       /* prepend csymbol */
-       bufptr -= strlen(csymbol);
-       memcpy(bufptr, csymbol, strlen(csymbol));
-
-       /* see if we need to signify negative amount */
-       if (minus)
-       {
-               result = palloc(strlen(bufptr) + strlen(nsymbol) + 3);
-
-               /* Position code of 0 means use parens */
-               if (convention == 0)
-                       sprintf(result, "(%s)", bufptr);
-               else if (convention == 2)
-                       sprintf(result, "%s%s", bufptr, nsymbol);
-               else
-                       sprintf(result, "%s%s", nsymbol, bufptr);
-       }
-       else
+       /*----------
+        * Now, attach currency symbol and sign symbol in the correct order.
+        *
+        * The POSIX spec defines these values controlling this code:
+        *
+        * p/n_sign_posn:
+        *      0       Parentheses enclose the quantity and the currency_symbol.
+        *      1       The sign string precedes the quantity and the currency_symbol.
+        *      2       The sign string succeeds the quantity and the currency_symbol.
+        *      3       The sign string precedes the currency_symbol.
+        *      4       The sign string succeeds the currency_symbol.
+        *
+        * p/n_cs_precedes: 0 means currency symbol after value, else before it.
+        *
+        * p/n_sep_by_space:
+        *      0       No <space> separates the currency symbol and value.
+        *      1       If the currency symbol and sign string are adjacent, a <space>
+        *              separates them from the value; otherwise, a <space> separates
+        *              the currency symbol from the value.
+        *      2       If the currency symbol and sign string are adjacent, a <space>
+        *              separates them; otherwise, a <space> separates the sign string
+        *              from the value.
+        *----------
+        */
+       switch (sign_posn)
        {
-               /* just emit what we have */
-               result = pstrdup(bufptr);
+               case 0:
+                       if (cs_precedes)
+                               result = psprintf("(%s%s%s)",
+                                                                 csymbol,
+                                                                 (sep_by_space == 1) ? " " : "",
+                                                                 bufptr);
+                       else
+                               result = psprintf("(%s%s%s)",
+                                                                 bufptr,
+                                                                 (sep_by_space == 1) ? " " : "",
+                                                                 csymbol);
+                       break;
+               case 1:
+               default:
+                       if (cs_precedes)
+                               result = psprintf("%s%s%s%s%s",
+                                                                 signsymbol,
+                                                                 (sep_by_space == 2) ? " " : "",
+                                                                 csymbol,
+                                                                 (sep_by_space == 1) ? " " : "",
+                                                                 bufptr);
+                       else
+                               result = psprintf("%s%s%s%s%s",
+                                                                 signsymbol,
+                                                                 (sep_by_space == 2) ? " " : "",
+                                                                 bufptr,
+                                                                 (sep_by_space == 1) ? " " : "",
+                                                                 csymbol);
+                       break;
+               case 2:
+                       if (cs_precedes)
+                               result = psprintf("%s%s%s%s%s",
+                                                                 csymbol,
+                                                                 (sep_by_space == 1) ? " " : "",
+                                                                 bufptr,
+                                                                 (sep_by_space == 2) ? " " : "",
+                                                                 signsymbol);
+                       else
+                               result = psprintf("%s%s%s%s%s",
+                                                                 bufptr,
+                                                                 (sep_by_space == 1) ? " " : "",
+                                                                 csymbol,
+                                                                 (sep_by_space == 2) ? " " : "",
+                                                                 signsymbol);
+                       break;
+               case 3:
+                       if (cs_precedes)
+                               result = psprintf("%s%s%s%s%s",
+                                                                 signsymbol,
+                                                                 (sep_by_space == 2) ? " " : "",
+                                                                 csymbol,
+                                                                 (sep_by_space == 1) ? " " : "",
+                                                                 bufptr);
+                       else
+                               result = psprintf("%s%s%s%s%s",
+                                                                 bufptr,
+                                                                 (sep_by_space == 1) ? " " : "",
+                                                                 signsymbol,
+                                                                 (sep_by_space == 2) ? " " : "",
+                                                                 csymbol);
+                       break;
+               case 4:
+                       if (cs_precedes)
+                               result = psprintf("%s%s%s%s%s",
+                                                                 csymbol,
+                                                                 (sep_by_space == 2) ? " " : "",
+                                                                 signsymbol,
+                                                                 (sep_by_space == 1) ? " " : "",
+                                                                 bufptr);
+                       else
+                               result = psprintf("%s%s%s%s%s",
+                                                                 bufptr,
+                                                                 (sep_by_space == 1) ? " " : "",
+                                                                 csymbol,
+                                                                 (sep_by_space == 2) ? " " : "",
+                                                                 signsymbol);
+                       break;
        }
 
        PG_RETURN_CSTRING(result);
@@ -793,7 +937,7 @@ cashsmaller(PG_FUNCTION_ARGS)
 }
 
 /* cash_words()
- * This converts a int4 as well but to a representation using words
+ * This converts an int4 as well but to a representation using words
  * Obviously way North American centric - sorry
  */
 Datum