
#include <stdlib.h>
#include <limits.h>
#include "postgres.h"
#include "fmgr.h"
#include "numeric.h"

/*
** PostgreSQL Numeric data type conversion functions
**
** Original implementation by Dann Corbit on 2-8-2002
**
** Since all of the digit groups *should* be in the range of 00 to 99,
** it may seem strange that these arrays are all dimensioned with
** UCHAR_MAX+1 (255+1=256 on most systems).  But in having an enlarged
** dimension, we are safeguarded against an accidental over run
** due to bad data, etc.  In any case, very little space is wasted
** (less than 1K in total).
** Instead of deciphering the digits one at a time, we just make the
** look up table with gaps instead so that we can look them up two
** digits [as encoded into one character] at a time.
*/

/*
If I collect a numeric entry like so:
        void *vp = PQgetvalue(result, r, c);
        int st   = PQgetlength(result, r, c);
        char       num_data[2000] ={0};
        int        err;

I then call the NumericUntangle() routine as follows:
        NumericUntangle(vp, st, num_data, sizeof num_data, &err);
*/

static const char *two_dig[UCHAR_MAX + 1] =
{
    "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "", "",
"", "", "", "",
    "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "", "",
"", "", "", "",
    "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "", "",
"", "", "", "",
    "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "", "",
"", "", "", "",
    "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "", "",
"", "", "", "",
    "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "", "",
"", "", "", "",
    "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "", "",
"", "", "", "",
    "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "", "",
"", "", "", "",
    "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "", "",
"", "", "", "",
    "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "", "",
"", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
};

static const char *two_p_dig[UCHAR_MAX + 1] =
{
    "0.0", "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8",
"0.9", "", "", "", "", "", "",
    "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8",
"1.9", "", "", "", "", "", "",
    "2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", "2.8",
"2.9", "", "", "", "", "", "",
    "3.0", "3.1", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7", "3.8",
"3.9", "", "", "", "", "", "",
    "4.0", "4.1", "4.2", "4.3", "4.4", "4.5", "4.6", "4.7", "4.8",
"4.9", "", "", "", "", "", "",
    "5.0", "5.1", "5.2", "5.3", "5.4", "5.5", "5.6", "5.7", "5.8",
"5.9", "", "", "", "", "", "",
    "6.0", "6.1", "6.2", "6.3", "6.4", "6.5", "6.6", "6.7", "6.8",
"6.9", "", "", "", "", "", "",
    "7.0", "7.1", "7.2", "7.3", "7.4", "7.5", "7.6", "7.7", "7.8",
"7.9", "", "", "", "", "", "",
    "8.0", "8.1", "8.2", "8.3", "8.4", "8.5", "8.6", "8.7", "8.8",
"8.9", "", "", "", "", "", "",
    "9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7", "9.8",
"9.9", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
};


static const char *p_two_dig[UCHAR_MAX + 1] =
{
    ".00", ".01", ".02", ".03", ".04", ".05", ".06", ".07", ".08",
".09", "", "", "", "", "", "",
    ".10", ".11", ".12", ".13", ".14", ".15", ".16", ".17", ".18",
".19", "", "", "", "", "", "",
    ".20", ".21", ".22", ".23", ".24", ".25", ".26", ".27", ".28",
".29", "", "", "", "", "", "",
    ".30", ".31", ".32", ".33", ".34", ".35", ".36", ".37", ".38",
".39", "", "", "", "", "", "",
    ".40", ".41", ".42", ".43", ".44", ".45", ".46", ".47", ".48",
".49", "", "", "", "", "", "",
    ".50", ".51", ".52", ".53", ".54", ".55", ".56", ".57", ".58",
".59", "", "", "", "", "", "",
    ".60", ".61", ".62", ".63", ".64", ".65", ".66", ".67", ".68",
".69", "", "", "", "", "", "",
    ".70", ".71", ".72", ".73", ".74", ".75", ".76", ".77", ".78",
".79", "", "", "", "", "", "",
    ".80", ".81", ".82", ".83", ".84", ".85", ".86", ".87", ".88",
".89", "", "", "", "", "", "",
    ".90", ".91", ".92", ".93", ".94", ".95", ".96", ".97", ".98",
".99", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
};


static const char *two_dig_p[UCHAR_MAX + 1] =
{
    "00.", "01.", "02.", "03.", "04.", "05.", "06.", "07.", "08.",
"09.", "", "", "", "", "", "",
    "10.", "11.", "12.", "13.", "14.", "15.", "16.", "17.", "18.",
"19.", "", "", "", "", "", "",
    "20.", "21.", "22.", "23.", "24.", "25.", "26.", "27.", "28.",
"29.", "", "", "", "", "", "",
    "30.", "31.", "32.", "33.", "34.", "35.", "36.", "37.", "38.",
"39.", "", "", "", "", "", "",
    "40.", "41.", "42.", "43.", "44.", "45.", "46.", "47.", "48.",
"49.", "", "", "", "", "", "",
    "50.", "51.", "52.", "53.", "54.", "55.", "56.", "57.", "58.",
"59.", "", "", "", "", "", "",
    "60.", "61.", "62.", "63.", "64.", "65.", "66.", "67.", "68.",
"69.", "", "", "", "", "", "",
    "70.", "71.", "72.", "73.", "74.", "75.", "76.", "77.", "78.",
"79.", "", "", "", "", "", "",
    "80.", "81.", "82.", "83.", "84.", "85.", "86.", "87.", "88.",
"89.", "", "", "", "", "", "",
    "90.", "91.", "92.", "93.", "94.", "95.", "96.", "97.", "98.",
"99.", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
};
/*
#define NUMERIC_SIGN_MASK	0xC000
#define NUMERIC_POS		0x0000
#define NUMERIC_NEG		0x4000
#define NUMERIC_NAN		0xC000
#define NUMERIC_DSCALE_MASK     0x3FFF
#define NUMERIC_SIGN(n)		((n)->n_sign_dscale & NUMERIC_SIGN_MASK)
#define NUMERIC_DSCALE(n)	((n)->n_sign_dscale &
NUMERIC_DSCALE_MASK)
#define NUMERIC_IS_NAN(n)	(NUMERIC_SIGN(n) != NUMERIC_POS &&
NUMERIC_SIGN(n) != NUMERIC_NEG)
*/

void            NumericUntangle(void *vp, int bytes_of_digits, char
*num_data, size_t num_data_len, int *err)
{
    /* It appears that the pointer to data returned by PQgetvalue() is
     * identical to a struct NumericData except that the varlen field is
     * missing. 
     */
    typedef struct tag_numeric_overlay {
        short           base_ten_exponent;
        short           decimal_shift_right;
        short           n_sign_dscale;
        unsigned char   digits[1];
    }               numeric_overlay;
    int             i;
    numeric_overlay *pno;
    pno = (numeric_overlay *) vp;
    char           *s = num_data;
    *err = 0;
    if (!NUMERIC_IS_NAN(pno)) {
        int             sign = NUMERIC_SIGN(pno);
        int             dscale = NUMERIC_DSCALE(pno);
        bytes_of_digits -= 3 * sizeof(short);
        /* If there is too much hamburger to shove into the can (IOW, we
         * have more bytes of data than we do of string) then we set the
         * error flag and fill the string with pound signs. (#)
         */
        if (num_data_len < (bytes_of_digits + 1) * 2) {
            memset(s, '#', num_data_len - 1);
            s[num_data_len - 1] = 0;
            *err = 1;
            return;
        }
        if (sign)
            strcpy(s, "-");
        else
            s[0] = 0;
        if (pno->base_ten_exponent < 0) {
            strcat(s, "0.");
            int             slen = strlen(s);
            int             zcount = abs(pno->base_ten_exponent) - 1;
            memset(s + slen, '0', zcount);
            s[slen + zcount + 1] = 0;
            for (i = 0; i < bytes_of_digits; i++)
                strcat(s, two_dig[pno->digits[i]]);
        } else {
            int             texp = pno->base_ten_exponent;
            int             bytes_to_go = bytes_of_digits;
            for (bytes_to_go = bytes_of_digits, i = 0; texp > 1; texp -=
2, bytes_to_go--, i++) {
                if (bytes_to_go)
                    strcat(s, two_dig[pno->digits[i]]);
                else
                    strcat(s, two_dig[pno->digits[0]]);
            }
            switch (texp) {
            case -1:
                if (bytes_to_go) {
                    strcat(s, p_two_dig[pno->digits[i]]);
                    bytes_to_go--;
                } else
                    strcat(s, p_two_dig[pno->digits[0]]);
                break;
            case 0:
                if (bytes_to_go) {
                    strcat(s, two_p_dig[pno->digits[i]]);
                    bytes_to_go--;
                } else
                    strcat(s, two_p_dig[pno->digits[0]]);
                break;
            case 1:
                if (bytes_to_go) {
                    strcat(s, two_dig_p[pno->digits[i]]);
                    bytes_to_go--;
                } else
                    strcat(s, two_dig_p[pno->digits[0]]);
                break;
            }
            texp -= 2;
            for (++i; texp > 1 || bytes_to_go > 0; texp -= 2,
bytes_to_go--, i++) {
                if (bytes_to_go)
                    strcat(s, two_dig[pno->digits[i]]);
                else
                    strcat(s, two_dig[pno->digits[0]]);
            }

        }
    } else { /* This is a NAN */
        strcpy(s, "#NAN#");
    }
}

Wed, 13 Feb 2002 02:58:14 -0800
"Dann Corbit" <DCorbit@connx.com>
Numeric data type conversion form binary cursor -- Am I all wet,
	or is this about right?