diff --git a/src/test/examples/testlibpq3.c b/src/test/examples/testlibpq3.c index 4f7b791388..68972f17f3 100644 --- a/src/test/examples/testlibpq3.c +++ b/src/test/examples/testlibpq3.c @@ -11,21 +11,56 @@ * CREATE SCHEMA testlibpq3; * SET search_path = testlibpq3; * SET standard_conforming_strings = ON; - * CREATE TABLE test1 (i int4, t text, b bytea); - * INSERT INTO test1 values (1, 'joe''s place', '\000\001\002\003\004'); - * INSERT INTO test1 values (2, 'ho there', '\004\003\002\001\000'); + * CREATE SCHEMA testlibpq3; + * SET search_path = testlibpq3; + * SET standard_conforming_strings = ON; + * CREATE TABLE test1 ( + * i int4 + * , r real + * , bo boolean + * , ts timestamp + * , t text + * , b bytea + * ); + * INSERT INTO test1 + * VALUES ( + * 1 + * , 3.141593 + * , true + * , '2000-01-01 00:00:02.414213' + * , 'joe''s place' + * , '\000\001\002\003\004' + * ); + * INSERT INTO test1 + * VALUES ( + * 2 + * , 1.618033 + * , false + * , '2000-01-01 00:00:01.465571' + * , 'ho there' + * , '\004\003\002\001\000' + * ); * * The expected output is: * * tuple 0: got * i = (4 bytes) 1 + * r = (4 bytes) 3.141593 + * bo = (1 bytes) 1 * t = (11 bytes) 'joe's place' * b = (5 bytes) \000\001\002\003\004 * * tuple 0: got * i = (4 bytes) 2 + * r = (4 bytes) 1.618033 + * bo = (1 bytes) 0 * t = (8 bytes) 'ho there' * b = (5 bytes) \004\003\002\001\000 + * + * General notes about this example: + * + * Use PQfnumber to avoid assumptions about field order in result but when + * getting the field values we ignore possibility they are null! */ #ifdef WIN32 @@ -36,6 +71,7 @@ #include #include #include +#include #include #include "libpq-fe.h" @@ -44,6 +80,13 @@ #include +/* These macros hopefully make reading calculations for timestamps easier. */ +#define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */ +#define UNIX_EPOCH_JDATE 2440588 /* == date2j(1970, 1, 1) */ +#define SECS_PER_DAY 86400 + +uint64_t ntohll(uint64_t); + static void exit_nicely(PGconn *conn) { @@ -51,6 +94,142 @@ exit_nicely(PGconn *conn) exit(1); } +static void +handle_boolean(PGresult *res, int i) +{ + int fnum; + char *ptr; + int val; + + fnum = PQfnumber(res, "bo"); + ptr = PQgetvalue(res, i, fnum); + val = (int) *ptr; + printf(" bo = (%d bytes) %d\n", PQgetlength(res, i, fnum), val); +} + +static void +handle_bytea(PGresult *res, int i) +{ + int j; + int fnum; + char *ptr; + int len; + + fnum = PQfnumber(res, "b"); + ptr = PQgetvalue(res, i, fnum); + + /* + * The binary representation of BYTEA is a bunch of bytes, which could + * include embedded nulls so we have to pay attention to field length. + */ + len = PQgetlength(res, i, fnum); + printf(" b = (%d bytes) ", len); + for (j = 0; j < len; j++) printf("\\%03o", ptr[j]); +} + +static void +handle_integer(PGresult *res, int i) +{ + int fnum; + char *ptr; + int val; + + fnum = PQfnumber(res, "i"); + ptr = PQgetvalue(res, i, fnum); + + /* + * The binary representation of INT4 is in network byte order, which + * we'd better coerce to the local byte order. + */ + val = ntohl(*((uint32_t *) ptr)); + + printf(" i = (%d bytes) %d\n", PQgetlength(res, i, fnum), val); +} + +static void +handle_real(PGresult *res, int i) +{ + int fnum; + char *ptr; + union { + int i; + float f; + } val; + + fnum = PQfnumber(res, "r"); + ptr = PQgetvalue(res, i, fnum); + + /* + * The binary representation of INT4 is in network byte order, which + * we'd better coerce to the local byte order. + */ + val.i = ntohl(*((uint32_t *) ptr)); + + printf(" r = (%d bytes) %f\n", PQgetlength(res, i, fnum), val.f); +} + +static void +handle_text(PGresult *res, int i) +{ + int fnum; + char *ptr; + + fnum = PQfnumber(res, "t"); + ptr = PQgetvalue(res, i, fnum); + + /* + * The binary representation of TEXT is, well, text, and since libpq was + * nice enough to append a zero byte to it, it'll work just fine as a C + * string. + */ + printf(" t = (%d bytes) '%s'\n", PQgetlength(res, i, fnum), ptr); +} + +static void +handle_timestamp(PGresult *res, int i) +{ + int fnum; + char *ptr; + uint64_t val; + + struct tm *tm; + time_t timep; + uint32_t mantissa; + + fnum = PQfnumber(res, "ts"); + ptr = PQgetvalue(res, i, fnum); + val = ntohll(*((uint64_t *) ptr)); + + /* + * The binary representation of a timestamp is in microseconds + * from 2000-01-01. + */ + timep = val / (uint64_t) 1000000 + + (uint64_t) (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * + (uint64_t) SECS_PER_DAY; + mantissa = val - (uint64_t) (timep - + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY) * + (uint64_t) 1000000; + + /* For ease of testing, assume and print timestamps in GMT. */ + tm = gmtime(&timep); + + printf(" ts = (%d bytes) %04d-%02d-%02d %02d:%02d:%02d.%06d\n", + PQgetlength(res, i, fnum), tm->tm_year + 1900, tm->tm_mon + 1, + tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, mantissa); +} + +/* This is a uint64_t version of ntohl from arpa/inet.h. */ +uint64_t +ntohll(uint64_t netlonglong) +{ + if (ntohl(1) == 1) + return netlonglong; + else + return (uint64_t) (ntohl((int) ((netlonglong << 32) >> 32))) << 32 | + (uint64_t) ntohl(((int) (netlonglong >> 32))); +} + /* * This function prints a query result that is a binary-format fetch from * a table defined as in the comment above. We split it out because the @@ -59,54 +238,17 @@ exit_nicely(PGconn *conn) static void show_binary_results(PGresult *res) { - int i, - j; - int i_fnum, - t_fnum, - b_fnum; - - /* Use PQfnumber to avoid assumptions about field order in result */ - i_fnum = PQfnumber(res, "i"); - t_fnum = PQfnumber(res, "t"); - b_fnum = PQfnumber(res, "b"); + int i; for (i = 0; i < PQntuples(res); i++) { - char *iptr; - char *tptr; - char *bptr; - int blen; - int ival; - - /* Get the field values (we ignore possibility they are null!) */ - iptr = PQgetvalue(res, i, i_fnum); - tptr = PQgetvalue(res, i, t_fnum); - bptr = PQgetvalue(res, i, b_fnum); - - /* - * The binary representation of INT4 is in network byte order, which - * we'd better coerce to the local byte order. - */ - ival = ntohl(*((uint32_t *) iptr)); - - /* - * The binary representation of TEXT is, well, text, and since libpq - * was nice enough to append a zero byte to it, it'll work just fine - * as a C string. - * - * The binary representation of BYTEA is a bunch of bytes, which could - * include embedded nulls so we have to pay attention to field length. - */ - blen = PQgetlength(res, i, b_fnum); - printf("tuple %d: got\n", i); - printf(" i = (%d bytes) %d\n", - PQgetlength(res, i, i_fnum), ival); - printf(" t = (%d bytes) '%s'\n", - PQgetlength(res, i, t_fnum), tptr); - printf(" b = (%d bytes) ", blen); - for (j = 0; j < blen; j++) - printf("\\%03o", bptr[j]); + handle_integer(res, i); + handle_real(res, i); + handle_boolean(res, i); + handle_timestamp(res, i); + handle_text(res, i); + handle_bytea(res, i); printf("\n\n"); } } diff --git a/src/test/examples/testlibpq3.sql b/src/test/examples/testlibpq3.sql index 35a95ca347..94cb2b97b0 100644 --- a/src/test/examples/testlibpq3.sql +++ b/src/test/examples/testlibpq3.sql @@ -1,6 +1,29 @@ CREATE SCHEMA testlibpq3; SET search_path = testlibpq3; SET standard_conforming_strings = ON; -CREATE TABLE test1 (i int4, t text, b bytea); -INSERT INTO test1 values (1, 'joe''s place', '\000\001\002\003\004'); -INSERT INTO test1 values (2, 'ho there', '\004\003\002\001\000'); +CREATE TABLE test1 ( + i int4 + , r real + , bo boolean + , ts timestamp + , t text + , b bytea +); +INSERT INTO test1 +VALUES ( + 1 + , 3.141593 + , true + , '2000-01-01 00:00:02.414213' + , 'joe''s place' + , '\000\001\002\003\004' +); +INSERT INTO test1 +VALUES ( + 2 + , 1.618033 + , false + , '2000-01-01 00:00:01.465571' + , 'ho there' + , '\004\003\002\001\000' +);