/* * This shows how to store values of type numeric in a PostgreSQL database * using C datatypes * * Change your database credentials as given to PQconnectdb(). * Then compile with: * cc -W -Wall -O3 -s -pipe -I/usr/local/include -L/usr/local/lib -o pgnumeric pgnumeric.c -lpq * * Create table for testing: * CREATE TABLE t1 (n numeric NOT NULL); */ #include #include #include #include /* for htons() */ #include /* PostgreSQL */ #include "libpq-fe.h" int main () { PGconn *pg = PQconnectdb("host=/tmp dbname=... user=... password=..."); PGresult *res; /* Keep in sync with struct NumericVar and numeric_send() from numeric.c: https://github.com/postgres/postgres/blob/master/src/backend/utils/adt/numeric.c */ struct { int16_t ndigits; /* # of digits in digits[] - can be 0! */ int16_t weight; /* weight of first digit */ int16_t sign; /* NUMERIC_POS, _NEG, _NAN, _PINF, or _NINF */ int16_t dscale; /* display scale */ /* XXX dont use a fixed size buffer */ int16_t buf[8]; /* start of palloc'd space for digits[] */ } NumericVar; int bin = 1, len, empty; uint8_t srcbuf[16]; uint16_t i, tmp, dstidx, srclen; uint32_t divi, modu; uint64_t ui64src, ui64tmp; /* We want to store these unsigned 64 bit and 72 bit integers as numerics */ ui64src = ~(uint64_t) 0; /* maximum uint64 */ srcbuf[0] = 1; /* maximum uint64 + 1 */ for (srclen = 1; srclen < 9; srclen++) srcbuf[srclen] = 0; /* First, store native C uint64 */ dstidx = 0; for (ui64tmp = ui64src; ui64tmp; ui64tmp /= 10000) { dstidx++; } if (dstidx >= sizeof(NumericVar.buf)/sizeof(NumericVar.buf[0])) errx(1, "%s", "NumericVar.buf too small for uint64"); NumericVar.ndigits = htons(dstidx); NumericVar.weight = htons(dstidx - 1); NumericVar.sign = 0; NumericVar.dscale = 0; len = 4 * 2 + dstidx * 2; // sizeof(int16_t) = 2 for (ui64tmp = ui64src; ui64tmp; ui64tmp /= 10000) { NumericVar.buf[--dstidx] = htons(ui64tmp % 10000); } if (PQstatus(pg) != CONNECTION_OK) errx(1, "%s", "PGconn"); res = PQexecParams(pg, "INSERT INTO t1 VALUES ($1)", 1, NULL, (const char*[]){ (char*)&NumericVar }, (const int[]){ len }, (const int[]){ bin }, 0); if (PQresultStatus(res) != PGRES_COMMAND_OK) errx(1, "%s", "INSERT"); /* Now store 72 bit unsigned integer */ dstidx = 0; do { empty = 1; modu = srcbuf[0]; for (i = 0; i < srclen - 1; i++) { divi = modu / 10000; modu = ((modu - divi * 10000) << 8) + srcbuf[i + 1]; srcbuf[i] = divi; empty &= (srcbuf[i] == 0); } divi = modu / 10000; modu = modu - divi * 10000; srcbuf[i] = divi; empty &= (srcbuf[i] == 0); if (dstidx >= sizeof(NumericVar.buf)/sizeof(NumericVar.buf[0])) errx(1, "%s", "NumericVar.buf too small for large integer"); NumericVar.buf[dstidx] = htons(modu); dstidx++; } while (!empty); for (i = 0; i < dstidx / 2; i++) { tmp = NumericVar.buf[i]; NumericVar.buf[i] = NumericVar.buf[dstidx - 1 - i]; NumericVar.buf[dstidx - 1 - i] = tmp; } NumericVar.ndigits = htons(dstidx); NumericVar.weight = htons(dstidx - 1); NumericVar.sign = 0; NumericVar.dscale = 0; len = 4 * 2 + dstidx * 2; // sizeof(int16_t) = 2 if (PQstatus(pg) != CONNECTION_OK) errx(1, "%s", "PGconn"); res = PQexecParams(pg, "INSERT INTO t1 VALUES ($1)", 1, NULL, (const char * const []){ (char*)&NumericVar }, (const int[]){ len }, (const int[]){ bin }, 0); if (PQresultStatus(res) != PGRES_COMMAND_OK) errx(1, "%s", "INSERT"); PQfinish(pg); }