libpqtypes - regression-test.c

Home Page

array.c
datetime.c
error.c
events.c
exec.c
geo.c
getaddrinfo.h
handler.c
libpqtypes-int.h
libpqtypes.h
misc.c
network.c
numerics.c
param.c
port.c
record.c
regression-test.c
spec.c
utils.c
varlena.c
   1 
   2 #ifdef HAVE_CONFIG_H
   3 # include "pqt_config.h"
   4 #endif
   5 
   6 #define PLN do{ \
   7   printf("%s:%d\n", __FUNCTION__, __LINE__); \
   8   fflush(stdout); \
   9 }while(0)
  10 
  11 #if defined(_WIN32) || defined(_WIN64)
  12 # define _CRT_SECURE_NO_WARNINGS /* shut up msvc8! */
  13 # ifdef _MSC_VER
  14 #   pragma warning (disable : 4127 4706)
  15 # endif
  16 # include <winsock2.h>
  17 # include <ws2tcpip.h> /* getnameinfo */
  18 # ifdef _MSC_VER
  19 #   define I64FMT "%I64d"
  20 # else
  21 #   define I64FMT "%lld"
  22 # endif
  23 #else
  24 # define SIGNAL_SUPPORT
  25 # include <netdb.h>
  26 # include <unistd.h>
  27 # include <sys/socket.h>
  28 # include <netinet/in.h>
  29 # define I64FMT "%lld"
  30 #endif
  31 
  32 #ifdef SIGNAL_SUPPORT
  33 # include <signal.h>
  34 #endif
  35 
  36 #include <libpqtypes.h>
  37 #include <stdlib.h>
  38 #include <string.h>
  39 #include <limits.h>
  40 #include <ctype.h>
  41 #include <time.h>
  42 #include <stdarg.h>
  43 #include <errno.h>
  44 
  45 #ifndef LLONG_MAX
  46 # ifdef LONG_LONG_MAX
  47 #   define LLONG_MAX LONG_LONG_MAX
  48 # else
  49 #   define LLONG_MAX 9223372036854775807LL
  50 # endif
  51 #endif
  52 
  53 #ifndef LLONG_MIN
  54 # ifdef LONG_LONG_MIN
  55 #   define LLONG_MIN LONG_LONG_MIN
  56 # else
  57 #   define LLONG_MIN -9223372036854775808LL
  58 # endif
  59 #endif
  60 
  61 #define RESERR PQresultErrorMessage(result)
  62 #define CLEAR_TABLE(tname) PQclear(PQexec(conn, "TRUNCATE " tname))
  63 #define DROP_TABLE(tname) \
  64   PQclear(PQexec(conn, "DROP TABLE IF EXISTS " tname))
  65 
  66 #define EXECOKAY(msg, status) do{ \
  67   if (PQresultStatus(result) != (status)) \
  68   { \
  69     fprintf(stderr, "  **ERROR: %s - %s\n", msg, RESERR); \
  70     PQclear(result); \
  71     failcnt++; \
  72     return; \
  73   } \
  74 }while (0)
  75 
  76 #define CMDOKAY(msg) EXECOKAY(msg, PGRES_COMMAND_OK)
  77 #define TUPSOKAY(msg) EXECOKAY(msg, PGRES_TUPLES_OK)
  78 
  79 #define PUTOKAY(paramobj, retval, msg) do{ \
  80   if ((retval) == 0){ \
  81     failcnt++; \
  82     fprintf(stderr, "  **ERROR: %s - %s\n", msg, PQgeterror()); \
  83     return; \
  84   } \
  85 }while (0)
  86 
  87 #define GETOKAY(retval, msg) GETOKAY2(result, retval, msg)
  88 #define GETOKAY2(_res, retval, msg) do{ \
  89   if ((retval) == 0){ \
  90     failcnt++; \
  91     fprintf(stderr, "  **ERROR: %s - %s\n", msg, PQgeterror()); \
  92     PQclear(result); \
  93     return; \
  94   } \
  95 }while (0)
  96 
  97 #define CHKNUM(typspec, getval, putval) do{ \
  98   if ((getval) != (putval)) { \
  99     failcnt++; \
 100     fprintf(stderr, "  %s - FAILED\n", typspec); \
 101   } \
 102   else \
 103     printf("  %s - passed\n", typspec); \
 104   testcnt++; \
 105 } while (0)
 106 
 107 #define CHKGEO(type) do{ \
 108   const char *typspec = "%" # type; \
 109   if (memcmp(& type, & type ## val, sizeof(type))) { \
 110     failcnt++; \
 111     fprintf(stderr, "  %s - FAILED\n", typspec); \
 112   } \
 113   else \
 114     printf("  %s - passed\n", typspec); \
 115   testcnt++; \
 116 } while (0)
 117 
 118 #define CHKVLEN(typ, resval, inval) do{ \
 119   testcnt++; \
 120   if (strcmp(resval, inval)) { \
 121     failcnt++; \
 122     fprintf(stderr, "  %%" typ " - FAILED\n"); \
 123   } \
 124   else \
 125     printf("  %%" typ " - passed\n"); \
 126 }while (0)
 127 
 128 static void cleanup(void);
 129 static void test_natives(int format);
 130 static void test_composite(void);
 131 static void test_geometrics(int format);
 132 static void test_varlen(int format);
 133 static void test_datetime(int format);
 134 static void test_network(int format);
 135 static int epoch_put(PGtypeArgs *args);
 136 static int epoch_get(PGtypeArgs *args);
 137 static void test_subclass(int format);
 138 static PGresult *execf(const char *cmdSpec, ...);
 139 static int bpcharcmp(const char *put, const char *get, int charlen);
 140 
 141 #ifdef SIGNAL_SUPPORT
 142 static void sighandler(int s);
 143 #endif
 144 
 145 static void _notice_processor(void *arg, const char *message)
 146 {/* do nothing */
 147   arg = NULL;
 148   message = NULL;
 149 }
 150 
 151 
 152 static int testcnt = 0;
 153 static int failcnt = 0;
 154 static const char *datestyle = "";
 155 static PGconn *conn = NULL;
 156 static PGparam *param = NULL;
 157 static PGresult *result = NULL;
 158 
 159 #define ALIGN_NUM(val, mult) (((val) + ((mult) - 1)) & ~(((mult) - 1)))
 160 
 161 int main(int argc, char **argv)
 162 {
 163   PGint4 n;
 164   PGregisterType regtype;
 165   char *conninfo = argc > 1 ? argv[1] : "hostaddr=127.0.0.1 user=postgres";
 166 
 167   conn = PQconnectdb(conninfo);
 168   if (PQstatus(conn) != CONNECTION_OK)
 169   {
 170     fprintf(stderr, "connection failure: %s\n", PQerrorMessage(conn));
 171     return 1;
 172   }
 173 
 174   PQsetNoticeProcessor(conn, _notice_processor, NULL);
 175 
 176   if (!PQinitTypes(conn))
 177   {
 178     fprintf(stderr, "Failed to initialize libpqtypes as "
 179       "a libpq event proc\n");
 180     PQfinish(conn);
 181     return 1;
 182   }
 183 
 184   testcnt++;
 185   regtype.typname = "bad_type";
 186   if(PQregisterTypes(conn, PQT_COMPOSITE, &regtype, 1, 0))
 187   {
 188     failcnt++;
 189     fprintf(stderr, "Test bad type register failure case (FAILED)\n");
 190   }
 191 
 192   regtype.typname = "mytype_t";
 193   if(!PQregisterTypes(conn, PQT_COMPOSITE, &regtype, 1, 0))
 194     fprintf(stderr, "%s\n", PQgeterror());
 195 
 196   test_composite();
 197 
 198   PQclear(PQexec(conn, "SET DateStyle TO 'ISO'"));
 199   param = PQparamCreate(conn);
 200 
 201   /* test PQputvf - result should be 10 */
 202   testcnt++;
 203   printf("\nPQputvf\n");
 204   result = execf("SELECT %int4 + (%int4 % %int4) AS answer", 9, 67, 3);
 205   if (PQresultStatus(result) != PGRES_TUPLES_OK)
 206   {
 207     fprintf(stderr, "  **ERROR: PQputvf - %s\n", PQgeterror());
 208     PQclear(result);
 209     failcnt++;
 210     return 1;
 211   }
 212 
 213   PQgetf(result, 0, "#int4", "answer", &n);
 214   PQclear(result);
 215   if (n != 10)
 216   {
 217     failcnt++;
 218     fprintf(stderr, "  PQputvf - FAILED\n");
 219   }
 220   else
 221   {
 222     printf("  PQputvf - passed\n");
 223   }
 224 
 225   testcnt++;
 226   printf("\nPQspecPrepare\n");
 227   if(!PQspecPrepare(conn, "test", "SELECT %int8 + %int4", 1))
 228   {
 229     failcnt++;
 230     fprintf(stderr, "  PQspecPrepare - FAILED (%s)\n", PQgeterror());
 231   }
 232   else
 233   {
 234     char data[4];
 235     PGbytea ba;
 236     PGint8 i8 = 45;
 237     ba.len = 4;
 238     ba.data = data;
 239 
 240     result = PQexecf(conn, "@test", i8, 4);
 241     if(!result)
 242     {
 243       failcnt++;
 244       fprintf(stderr, "  PQspecPrepare - FAILED (%s)\n", PQgeterror());
 245     }
 246     else
 247     {
 248       PGint8 i8out=0;
 249 
 250       PQgetf(result, 0, "%int8", 0, &i8out);
 251 
 252       if(i8 + 4 != i8out)
 253       {
 254         failcnt++;
 255         fprintf(stderr, "  PQspecPrepare - FAILED (expected "
 256           "%lld but got %lld)\n", i8+4, i8out);
 257       }
 258       else
 259       {
 260         printf("  PQspecPrepare - passed\n");
 261       }
 262 
 263       PQclear(result);
 264     }
 265   }
 266 
 267   datestyle = PQparameterStatus(conn, "DateStyle");
 268   if (!datestyle)
 269     datestyle = "";
 270 
 271 #ifdef SIGNAL_SUPPORT
 272   signal(SIGINT, sighandler);
 273   signal(SIGILL, sighandler);
 274   signal(SIGSEGV, sighandler);
 275 # ifdef SIGFPE
 276   signal(SIGFPE, sighandler);
 277 # endif
 278 # ifdef SIGBUS
 279   signal(SIGBUS, sighandler);
 280 # endif
 281 #endif
 282 
 283 
 284   test_natives(0);
 285   test_natives(1);
 286   test_varlen(0);
 287   test_varlen(1);
 288   test_geometrics(0);
 289   test_geometrics(1);
 290   test_network(0);
 291   test_network(1);
 292 
 293   regtype.typname = "libpq.epoch=pg_catalog.timestamptz";
 294   regtype.typput = epoch_put;
 295   regtype.typget = epoch_get;
 296 
 297   /* Test type sub-classing, use the epoch example from docs */
 298   if (!PQregisterTypes(conn, PQT_SUBCLASS, &regtype, 1, 0))
 299   {
 300     fprintf(stderr, "PQregisterTypes(epoch, subclass): %s\n",
 301       PQerrorMessage(conn));
 302   }
 303   else
 304   {
 305     test_subclass(0);
 306     test_subclass(1);
 307   }
 308 
 309   test_datetime(0);
 310   test_datetime(1);
 311 
 312   /* datestyle is cached in PGparam.  Since we need to change the datestyle,
 313    * we destroy the current param and recreate it later.
 314    */
 315   PQparamClear(param);
 316 
 317   /* Just tested ISO, test a non-ISO */
 318   PQclear(PQexec(conn, "SET DateStyle TO 'POSTGRES'"));
 319   datestyle = PQparameterStatus(conn, "DateStyle");
 320   if (!datestyle)
 321     datestyle = "";
 322 
 323   param = PQparamCreate(conn);
 324   test_datetime(0);
 325   test_datetime(1);
 326   cleanup();
 327 
 328   printf("\nRan %d tests - %d failed\n", testcnt, failcnt);
 329   return 1;
 330 }
 331 
 332 /* Calls putf and executes cmdSpec. Returns a result object */
 333 static PGresult *execf(const char *cmdSpec, ...)
 334 {
 335   int n;
 336   PGresult *res;
 337   va_list ap;
 338   char stmt[4096];
 339   PGparam *prm = PQparamCreate(conn);
 340 
 341   va_start(ap, cmdSpec);
 342   n = PQputvf(prm, stmt, sizeof(stmt), cmdSpec, ap);
 343   va_end(ap);
 344 
 345   /* error message in PQparamErrorMessage */
 346   if (!n)
 347   {
 348     PQparamClear(prm);
 349     return NULL;
 350   }
 351 
 352   printf("  %s\n", stmt);
 353   res = PQparamExec(conn, prm, stmt, 1);
 354   PQparamClear(prm);
 355   return res;
 356 }
 357 
 358 /* Handle BPCHAR (Blank-Padded) SQL-type "character(n)" */
 359 static int bpcharcmp(const char *put, const char *get, int charlen)
 360 {
 361   int i;
 362   int len = (int)strlen(put);
 363 
 364   for (i=len; i < charlen; i++)
 365     if (!isspace(get[i]))
 366       return 1;
 367 
 368   return memcmp(put, get, len);
 369 }
 370 
 371 static void cleanup(void)
 372 {
 373   DROP_TABLE("libpq_natives");
 374   DROP_TABLE("libpq_varlen");
 375   DROP_TABLE("libpq_geos");
 376   DROP_TABLE("libpq_network");
 377   DROP_TABLE("libpq_datetime");
 378   DROP_TABLE("libpq_subclass");
 379   DROP_TABLE("libpq_composite");
 380   PQclear(PQexec(conn, "DROP TYPE complex"));
 381   PQclear(PQexec(conn, "DROP TYPE simple"));
 382   PQparamClear(param);
 383   PQfinish(conn);
 384 }
 385 
 386 /* make an effort to clean up */
 387 #ifdef SIGNAL_SUPPORT
 388 static void sighandler(int s)
 389 {
 390   cleanup();
 391   fprintf(stderr, "\n**Caught signal: %d ... aborting\n", s);
 392   exit(1);
 393 }
 394 #endif
 395 
 396 static void test_composite(void)
 397 {
 398   int i,x,r;
 399   int ntups;
 400   int inttups;
 401   PGtext text;
 402   PGparam *simple;
 403   PGparam *complex;
 404   PGparam *prm;
 405   PGresult *simple_res;
 406   PGarray intarr;
 407   PGarray comparr;
 408   PGchar buf[64];
 409   PGint4 a;
 410   PGint8 b;
 411   PGregisterType types[] = {
 412     {"public.simple", NULL, NULL},
 413     {"public.complex", NULL, NULL}
 414   };
 415 
 416   printf("\nComposites & Arrays: (always binary)\n");
 417 
 418   printf("  Testing empty array handling ");
 419   PQclear(PQexec(conn, "DROP TABLE libpq_array"));
 420   result = PQexec(conn, "CREATE TABLE libpq_array (arr int[])");
 421   CMDOKAY("creating libpq_array table:");
 422   PQclear(result);
 423 
 424   result = PQexec(conn, "INSERT INTO libpq_array VALUES ('{}')");
 425   CMDOKAY("inserting into libpq_array:");
 426   PQclear(result);
 427 
 428   result = PQparamExec(conn, NULL, "SELECT * FROM libpq_array", 1);
 429   TUPSOKAY("executing select on libpq_array:");
 430 
 431   r = PQgetf(result, 0, "%int4[]", 0, &intarr);
 432 
 433   GETOKAY(r, "Failed to get an empty array");
 434   PQclear(result);
 435 
 436   /* both values should be 0 */
 437   testcnt++;
 438   if(PQntuples(intarr.res) != 0 || intarr.ndims != 0)
 439   {
 440     fprintf(stderr, "- FAILED\n    ***ERROR: tuple and dimension "
 441       "count should both be zero - ntups=%d, ndims=%d\n",
 442       PQntuples(intarr.res), intarr.ndims);
 443     failcnt++;
 444   }
 445   else
 446   {
 447     printf("- passed\n");
 448   }
 449   PQclear(intarr.res);
 450 
 451   DROP_TABLE("libpq_composite");
 452   PQclear(PQexec(conn, "DROP TYPE public.complex"));
 453 
 454   PQclear(PQexec(conn, "DROP TYPE public.simple"));
 455   result = PQexec(conn, "CREATE TYPE simple AS (a int4, b int8)");
 456   CMDOKAY("creating simple type");
 457   PQclear(result);
 458 
 459   result = PQexec(conn, "CREATE TYPE complex AS (t text, s simple, "
 460     "arr int4[][])");
 461   CMDOKAY("creating complex type");
 462   PQclear(result);
 463 
 464   if (!PQregisterTypes(conn, PQT_COMPOSITE, types, 2, 0))
 465   {
 466     fprintf(stderr, "  **ERROR: PQregisterTypes(%s %s): %s\n",
 467       types[0].typname, types[1].typname, PQgeterror());
 468     failcnt++;
 469     return;
 470   }
 471 
 472   result = PQexec(conn, "CREATE TABLE libpq_composite (comparr complex[])");
 473   CMDOKAY("creating libpq_composite table:");
 474   PQclear(result);
 475 
 476   simple = PQparamCreate(conn);
 477   complex = PQparamCreate(conn);
 478   prm = PQparamCreate(conn);
 479 
 480   /* array[10][3] */
 481   intarr.ndims     = 2;
 482   intarr.dims[0]   = 10;
 483   intarr.dims[1]   = 3;
 484   intarr.lbound[0] = 1;
 485   intarr.lbound[1] = 1;
 486   intarr.param = PQparamCreate(conn);
 487 
 488   /* If a 1D array is being used, you can avoid setting the dimension
 489    * members of the PGarray structure by zeroing it.
 490    */
 491   memset(&comparr, 0, sizeof(PGarray));
 492   comparr.param = PQparamCreate(conn);
 493 
 494   /* generate a complex[] containing 100 elements */
 495   for (i=0; i < 100; i++)
 496   {
 497     PGint8 val = ((PGint8)1<<48) + (PGint8)i;
 498 
 499     /* pack the fields of the 'simple' composite */
 500     r = PQputf(simple, "%int4 %int8", i+1000000, val);
 501     PUTOKAY(simple, r, "putting simple attributes");
 502 
 503     /* populate the complex.arr[10][3] */
 504     PQputf(intarr.param, "%null");
 505     for (x=1; x < 30; x++)
 506     {
 507       r= PQputf(intarr.param, "%int4", x+1);
 508       PUTOKAY(intarr.param, r, "putting complex.arr element");
 509     }
 510 
 511     /* pack the fields of the 'complex' composite */
 512     sprintf((char *) buf, "text_%03d", i+1);
 513     r = PQputf(complex, "%text %simple %int4[]", buf, simple, &intarr);
 514     PUTOKAY(complex, r, "putting complex attributes");
 515 
 516     /* now put element 'i' into the composite array */
 517     r = PQputf(comparr.param, "%complex", complex);
 518     PUTOKAY(comparr.param, r, "putting complex[] element");
 519 
 520     /* make sure to reset the params, otherwise you will continue to
 521      * append parameters to below params.  DO NOT reset the comparr.param,
 522      * as this is the array we want to insert.
 523      */
 524     PQparamReset(simple);
 525     PQparamReset(intarr.param);
 526     PQparamReset(complex);
 527 
 528     /* sneak in a NULL element.  NULL composite array items
 529      * used to be a bug.
 530      */
 531     if(i == 49)
 532     {
 533       PQputf(comparr.param, "%complex", NULL);
 534       i++;
 535     }
 536   }
 537 
 538   /* insert the complex[] */
 539   PQputf(prm, "%complex[]", &comparr);
 540   result = PQparamExec(conn, prm,
 541     "INSERT INTO libpq_composite VALUES ($1)", 1);
 542   CMDOKAY("inserting into libpq_composite");
 543   PQclear(result);
 544 
 545   /* clear all params (order doesn't matter, they are
 546    * isolated structures)
 547    */
 548   PQparamClear(simple);
 549   PQparamClear(intarr.param);
 550   PQparamClear(complex);
 551   PQparamClear(comparr.param);
 552   PQparamClear(prm);
 553 
 554   /* select complex[] back out */
 555   result = PQparamExec(conn, NULL, "SELECT * FROM libpq_composite", 1);
 556   TUPSOKAY("PQparamExec(SELECT:composite[])");
 557 
 558   /* getf it into a PGarray */
 559   r = PQgetf(result, 0, "%complex[]", 0, &comparr);
 560   GETOKAY(r, "PQgetf(complex[])");
 561   PQclear(result);
 562 
 563   /* composite arrays come back 1 tuple per element and 1
 564    * field per attribute
 565    */
 566   ntups = PQntuples(comparr.res);
 567   if (ntups != 100)
 568   {
 569     failcnt++;
 570     fprintf(stderr, "put 100 elements into complex[] but "
 571       "got back %d\n", ntups);
 572     PQclear(comparr.res);
 573     return;
 574   }
 575 
 576   testcnt++;
 577   for (i=0; i < ntups; i++)
 578   {
 579     /* Verify element 50 is NULL */
 580     if(i==50)
 581     {
 582       /* All fields should be NULL */
 583       if(!PQgetisnull(comparr.res, i, 0) ||
 584          !PQgetisnull(comparr.res, i, 1) ||
 585          !PQgetisnull(comparr.res, i, 2))
 586       {
 587         failcnt++;
 588         fprintf(stderr, "  Element 50 should of been NULL\n");
 589         PQclear(comparr.res);
 590         return;
 591       }
 592 
 593       continue;
 594     }
 595 
 596     /* get the complex composite at tuple 'i' */
 597     r = PQgetf(comparr.res, i, "%text %simple #int4[]",
 598       0, &text, 1, &simple_res, "arr", &intarr);
 599     GETOKAY2(comparr.res, r, "PQgetf(comparr.res element)");
 600 
 601     /* check the text field */
 602     sprintf((char *) buf, "text_%03d", i+1);
 603     if (strcmp(text, (char *) buf))
 604     {
 605       failcnt++;
 606       fprintf(stderr, "complex[%d].t failed: sent=%s, recv=%s\n",
 607         i, buf, text);
 608       PQclear(simple_res);
 609       PQclear(intarr.res);
 610       PQclear(comparr.res);
 611       return;
 612     }
 613 
 614     /* Check the simple field: reference by fname, use '#'
 615      * rather than '%'
 616      */
 617     r = PQgetf(simple_res, 0, "#int4 #int8", "a", &a, "b", &b);
 618     GETOKAY2(simple_res, r, "PQgetf(complex[].simple.*)");
 619     PQclear(simple_res);
 620 
 621     /* verify simple.* values */
 622     if (a != (i+1000000) || b != ((PGint8) 1<<48)+i)
 623     {
 624       failcnt++;
 625       fprintf(stderr, "complex[%d].simple.* failed: "
 626         "sent(a=%d, b=%lld), recv(a=%d, b=%lld)\n",
 627         i, i+1000000, ((PGint8) 1<<48)+i, a, b);
 628       PQclear(intarr.res);
 629       PQclear(comparr.res);
 630       return;
 631     }
 632 
 633     /* check the int[10][3] field */
 634     if (intarr.dims[0] != 10 || intarr.dims[1] != 3 ||
 635        intarr.lbound[0] != 1 || intarr.lbound[1] != 1 || intarr.ndims != 2)
 636     {
 637       failcnt++;
 638       fprintf(stderr, "complex[%d].int[][] dimension info failed: "
 639         "sent(ndims=2, arr[1:10][1:3]), recv(ndims=%d, arr[%d:%d][%d:%d])\n",
 640         i, intarr.ndims, intarr.lbound[0], intarr.dims[0],
 641         intarr.lbound[1], intarr.dims[1]);
 642       PQclear(intarr.res);
 643       PQclear(comparr.res);
 644       return;
 645     }
 646 
 647     inttups = PQntuples(intarr.res);
 648     for (x=1; x < inttups; x++)
 649     {
 650       r = PQgetf(intarr.res, x, "%int4", 0, &a);
 651       GETOKAY2(intarr.res, r, "PQgetf(complex.int[][] element");
 652 
 653       if (a != x+1)
 654       {
 655         failcnt++;
 656         fprintf(stderr, "  **ERROR: complex[%d].int4[][] sent=%d, recv=%d\n",
 657           i, x+1, a);
 658         PQclear(intarr.res);
 659         PQclear(comparr.res);
 660         return;
 661       }
 662     }
 663 
 664     PQclear(intarr.res);
 665   }
 666 
 667   PQclear(comparr.res);
 668   printf("  composite[] with nested composite and arrays passed\n");
 669 }
 670 
 671 static void test_natives(int format)
 672 {
 673   int r;
 674   PGchar ca;
 675   PGchar cb;
 676   PGint2 i2a, i2b;
 677   PGint4 i4a, i4b;
 678   PGint8 i8a, i8b;
 679   PGfloat4 f4a, f4b;
 680   PGfloat8 f8a, f8b;
 681   PGmoney mona, monb;
 682   Oid oida, oidb;
 683   PGbool boola, boolb;
 684 
 685   PQparamReset(param);
 686   printf("\nNative C types: (%s)\n", format ? "binary" : "text");
 687 
 688   r = PQputf(param, "%char %char %int2 %int2 %int4 %int4 %int8 %int8 "
 689     "%float4 %float4 %float8 %float8 %money %money %oid %oid %bool %bool",
 690     -117, 224, -32000, 32000, INT_MIN, INT_MAX, LLONG_MIN, LLONG_MAX,
 691     -1.23456f, 1.23456f, -123456789.654321, 123456789.654321,
 692     (PGmoney)INT_MIN, 600000000054LL, 91982, 3000000000U, 0, 1);
 693   PUTOKAY(param, r, "PQputf(natives)");
 694 
 695   DROP_TABLE("libpq_natives");
 696 
 697   result = PQexec(conn, "CREATE TABLE libpq_natives ("
 698     "char_a \"char\", char_b \"char\", "
 699     "i2_a int2, i2_b int2, "
 700     "i4_a int4, i4_b int4, "
 701     "i8_a int8, i8_b int8, "
 702     "f4_a float4, f4_b float4, "
 703     "f8_a float8, f8_b float8, "
 704     "mon_a money, mon_b money, "
 705     "oid_a oid, oid_b oid, "
 706     "bool_a bool, bool_b bool)");
 707   CMDOKAY("creating libpq_natives table");
 708   PQclear(result);
 709 
 710   result = PQparamExec(conn, param, "INSERT INTO libpq_natives VALUES"
 711     "($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18)",
 712     format);
 713   CMDOKAY("PQparamExec(INSERT:natives)");
 714   PQclear(result);
 715 
 716   result = PQparamExec(conn, NULL, "SELECT * FROM libpq_natives", format);
 717   TUPSOKAY("PQparamExec(SELECT:natives)");
 718 
 719   r = PQgetf(result, 0, "%char %char %int2 %int2 %int4 %int4 %int8 %int8 "
 720     "%float4 %float4 %float8 %float8 %money, %money %oid %oid %bool %bool",
 721     0, &ca, 1, &cb, 2, &i2a, 3, &i2b, 4, &i4a, 5, &i4b, 6, &i8a, 7, &i8b, 8,
 722     &f4a, 9, &f4b, 10, &f8a, 11, &f8b, 12, &mona, 13, &monb, 14, &oida,
 723     15, &oidb, 16, &boola, 17, &boolb);
 724   GETOKAY(r, "PQgetf(natives)");
 725   PQclear(result);
 726 
 727   CHKNUM("%char",   ca,    -117);
 728   CHKNUM("%char",   (unsigned char) cb,    224);
 729   CHKNUM("%int2",   i2a,   -32000);
 730   CHKNUM("%int2",   i2b,   32000);
 731   CHKNUM("%int4",   i4a,   INT_MIN);
 732   CHKNUM("%int4",   i4b,   INT_MAX);
 733   CHKNUM("%int8",   i8a,   LLONG_MIN);
 734   CHKNUM("%int8",   i8b,   LLONG_MAX);
 735   CHKNUM("%float4", f4a,   -1.23456f);
 736   CHKNUM("%float4", f4b,   1.23456f);
 737   CHKNUM("%float8", f8a,   -123456789.654321);
 738   CHKNUM("%float8", f8b,   123456789.654321);
 739   CHKNUM("%money",  mona,  (PGmoney) INT_MIN);
 740   CHKNUM("%money",  monb,  600000000054LL);
 741   CHKNUM("%oid",    oida,  91982);
 742   CHKNUM("%oid",    oidb,  3000000000U);
 743   CHKNUM("%bool",   boola, 0);
 744   CHKNUM("%bool",   boolb, 1);
 745 }
 746 
 747 static void test_geometrics(int format)
 748 {
 749   int r;
 750   static PGpoint ptarr[6] = {{565677.342, 999112.33344},
 751     {-565677.342, -999112.33344}, {-565677.342, 999112.33344},
 752     {565677.342, 999112.33344}, {-565677.342, -999112.33344},
 753     {-565677.342, 999112.33344}};
 754   PGpoint point = {0};
 755   PGpoint pointval = {565677.342, 999112.33344};
 756   PGlseg lseg = {{{0}}};
 757   PGlseg lsegval = {{{565677.342, -999112.33344}, {88876.2314, 77283.323}}};
 758   PGbox box = {{0}};
 759   PGbox boxval = {{565677.342, 88876.2314}, {77283.323, -999112.33344}};
 760   PGcircle circle = {{0,0},0};
 761   PGcircle circleval = {{7765.435, -7743.2993}, 23384.102};
 762   PGpath path = {0, 0, NULL};
 763   PGpath pathval = {6, 1, ptarr};
 764   PGpolygon polygon = {0, NULL};
 765   PGpolygon polygonval = {6, ptarr};
 766 
 767   PQparamReset(param);
 768   printf("\nGeometric types: (%s)\n", format ? "binary" : "text");
 769 
 770   r = PQputf(param, "%point %lseg %box %circle %path %polygon",
 771     &pointval, &lsegval, &boxval, &circleval, &pathval, &polygonval);
 772   PUTOKAY(param, r, "PQputf(geos)");
 773 
 774   DROP_TABLE("libpq_geos");
 775 
 776   result = PQexec(conn, "CREATE TABLE libpq_geos ("
 777     "p point, l lseg, b box, c circle, pa path, poly polygon)");
 778   CMDOKAY("creating libpq_geos table");
 779   PQclear(result);
 780 
 781   result = PQparamExec(conn, param, "INSERT INTO libpq_geos VALUES"
 782     "($1,$2,$3,$4,$5,$6)", format);
 783   CMDOKAY("PQparamExec(INSERT:geos)");
 784   PQclear(result);
 785 
 786   result = PQparamExec(conn, NULL, "SELECT * FROM libpq_geos", format);
 787   TUPSOKAY("PQparamExec(SELECT:geos)");
 788 
 789   r = PQgetf(result, 0, "%point %lseg %box %circle %path %polygon",
 790     0, &point, 1, &lseg, 2, &box, 3, &circle, 4, &path, 5, &polygon);
 791   GETOKAY(r, "PQgetf(geos)");
 792 
 793   CHKGEO(point);
 794   CHKGEO(lseg);
 795   CHKGEO(box);
 796   CHKGEO(circle);
 797 
 798   testcnt++;
 799   if (path.closed == pathval.closed && path.npts == pathval.npts &&
 800      memcmp(path.pts, pathval.pts, path.npts * sizeof(PGpoint))==0)
 801     printf("  %%path - passed\n");
 802   else
 803   {
 804     failcnt++;
 805     fprintf(stderr, "  %%path - FAILED\n");
 806   }
 807 
 808   testcnt++;
 809   if (polygon.npts == polygon.npts &&
 810      memcmp(polygon.pts, polygon.pts, polygon.npts * sizeof(PGpoint))==0)
 811     printf("  %%polygon - passed\n");
 812   else
 813   {
 814     failcnt++;
 815     fprintf(stderr, "  %%polygon - FAILED\n");
 816   }
 817 
 818   /* NOTE: this will invalidate path.pts and polygon.pts */
 819   PQclear(result);
 820 }
 821 
 822 /*
 823  * numeric is tested here because it is always exposed as a string.
 824  * uuid is not a varlen data type, but testing it here is ok
 825  */
 826 static void test_varlen(int format)
 827 {
 828   int r;
 829   static char byteadata[] = {0, 1, 2, 3, 4, 5, 6, 7};
 830   PGbpchar bpcharp=NULL;
 831   PGbpchar bpcharin = "character(n) blank-padded";
 832   PGvarchar varcharp=NULL;
 833   PGvarchar varcharin = "character varying(n)";
 834   PGtext textp=NULL;
 835   PGtext textin = "variable unlimited length";
 836   PGbytea byteap;
 837   PGbytea byteain = {8, byteadata};
 838   PGuuid uuid;
 839   char uuidin[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
 840   PGnumeric num;
 841   PGnumeric numin =
 842     "-62731893541288039212143296120112.12431212671229121291821928918211";
 843 
 844   PQparamReset(param);
 845   printf("\nVariable-length types: (%s)\n", format ? "binary" : "text");
 846 
 847   r = PQputf(param, "%bpchar %bpchar* %varchar %varchar* "
 848     "%text %text* %bytea %bytea* %uuid %numeric",
 849     bpcharin, bpcharin, varcharin, varcharin, textin, textin,
 850     &byteain, &byteain, uuidin, numin);
 851   PUTOKAY(param, r, "PQputf(varlen)");
 852 
 853   DROP_TABLE("libpq_varlen");
 854 
 855   result = PQexec(conn, "CREATE TABLE libpq_varlen ("
 856     "bp_a bpchar(32), bp_b bpchar(32), vc_a varchar(32), vc_b varchar(32), "
 857     "text_a text, text_b text, bytea_a bytea, bytea_b bytea, "
 858     "uid uuid, n numeric)");
 859   CMDOKAY("creating libpq_varlen table");
 860   PQclear(result);
 861 
 862   result = PQparamExec(conn, param, "INSERT INTO libpq_varlen VALUES"
 863     "($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)", format);
 864   CMDOKAY("PQparamExec(INSERT:varlen)");
 865   PQclear(result);
 866 
 867   result = PQparamExec(conn, NULL,
 868     "SELECT bp_a,vc_a,text_a,bytea_a,uid,n FROM libpq_varlen", format);
 869   TUPSOKAY("PQparamExec(SELECT:varlen)");
 870 
 871   r = PQgetf(result, 0,
 872     "%bpchar %varchar %text %bytea %uuid %numeric",
 873     0, &bpcharp,     /* field_num, PGbpchar* */
 874     1, &varcharp,    /* field_num, PGvarchar* */
 875     2, &textp,       /* field_num, PGtext* */
 876     3, &byteap,      /* field_num, PGbytea* */
 877     4, &uuid,        /* field_num, PGuuid* */
 878     5, &num);        /* field_num, PGnumeric* */
 879   GETOKAY(r, "PQgetf(varlen)");
 880 
 881   /* Because we are using the '*' specifier flag, we clear the results at
 882    * the end.  '*' flag is just a pointer to the result: like PQgetvalue().
 883    */
 884 
 885   testcnt++;
 886   if (bpcharcmp(bpcharin, bpcharp, 32))
 887   {
 888     failcnt++;
 889     fprintf(stderr, "  %%bpchar - FAILED\n");
 890   }
 891   else
 892     printf("  %%bpchar - passed\n");
 893 
 894   CHKVLEN("varchar",  varcharp, varcharin);
 895   CHKVLEN("text",     textp, textin);
 896 
 897   testcnt++;
 898   if (byteain.len != byteap.len ||
 899      memcmp(byteap.data, byteain.data, byteap.len))
 900   {
 901     failcnt++;
 902     fprintf(stderr, "  %%bytea* - FAILED\n");
 903   }
 904   else
 905     printf("  %%bytea* - passed\n");
 906 
 907   testcnt++;
 908   if (memcmp(uuid, uuidin, 16))
 909   {
 910     failcnt++;
 911     fprintf(stderr, "  %%uuid - FAILED\n");
 912   }
 913   else
 914     printf("  %%uuid - passed\n");
 915 
 916   CHKVLEN("numeric", num, numin);
 917   PQclear(result);
 918 }
 919 
 920 static void test_datetime(int format)
 921 {
 922   int r;
 923   time_t t;
 924   struct tm *tm;
 925   char *tzn;
 926   PGdate date[3] = {{0}};
 927   static PGdate dateval[] = {
 928     {0, 1476,  9,  1, 2260432, 274, 0},         /* 1476-10-01 */
 929     {1, 1209,  7, 17, 1280076, 229, 1},         /* 1209-08-17 BC */
 930     {0, 2007, 11, 23, 2454458, 356, 0}};        /* 2007-12-23 */
 931   PGtime tim[3] = {{0}};
 932   static PGtime timeval[] = {
 933     {23, 10, 31,   7214, 0, -1, 0, {0}},        /* 23:10:31.007214 */
 934     { 2, 19, 52,      0, 0, -1, 0, {0}},        /* 02:19:52 */
 935     {15, 57,  7, 881542, 0, -1, 0, {0}}};       /* 15:57:07.881542 */
 936   PGtime timetz[3] = {{0}};
 937   PGtime timetzval[] = {
 938     {23, 10, 31,   9888, 1, -1, -28800, {0}},   /* 23:10:31.009888-08 */
 939     { 2, 19, 52,      0, 1, -1,   7200, {0}},   /* 02:19:52+02 */
 940     {15, 57,  7, 881542, 1, -1, -10800, {0}}};  /* 15:57:07.881542-03 */
 941   PGtimestamp timestamp[3] = {{0, {0}, {0}}};
 942   PGtimestamp timestampval[] = {
 943     { -15565394969, {0, 1476,  9,  1, 2260432, 274, 0},
 944       {23, 10, 31, 7214, 0, -1, 0, {0}}},   /* 1476-10-01 23:10:31.007214 */
 945     {-100268228408, {1, 1209,  7, 17, 1280076, 229, 1},
 946       { 2, 19, 52, 0, 0, -1, 0, {0}}},    /* 1209-08-17 02:19:52 BC */
 947     {   1198425427, {0, 2007, 11, 23, 2454458, 356, 0},
 948       {15, 57, 7, 881542, 0, -1, 0, {0}}}}; /* 2007-12-23 15:57:07.881542 */
 949   PGinterval interval = {0};
 950   PGinterval intervalval = /* 2,354 years 8 mons 23 days 14:54:27.029333 */
 951     {2354, 8, 23, 14, 54, 27, 29333};
 952   PGtimestamp timestamptz;
 953   PGtimestamp timestamptzval;
 954 
 955   PQparamReset(param);
 956   memset(&timestamptz, 0, sizeof(PGtimestamp));
 957   memset(&timestamptzval, 0, sizeof(PGtimestamp));
 958 
 959   /* Jan 1, 1980 (returned: text=server time zone, binary=localtime) */
 960   t = 315532800;
 961   tm = localtime(&t);
 962   timestamptzval.epoch       = t;
 963   timestamptzval.time.hour   = tm->tm_hour;
 964   timestamptzval.time.min    = tm->tm_min;
 965   timestamptzval.time.sec    = tm->tm_sec;
 966   timestamptzval.time.usec   = 0;
 967   timestamptzval.time.withtz = 1;
 968   timestamptzval.date.isbc   = 0;
 969   timestamptzval.date.year   = tm->tm_year + 1900;
 970   timestamptzval.date.mon    = tm->tm_mon;
 971   timestamptzval.date.mday   = tm->tm_mday;
 972   PQlocalTZInfo(&t, &timestamptzval.time.gmtoff,
 973     &timestamptzval.time.isdst, &tzn);
 974   strcpy(timestamptzval.time.tzabbr, tzn);
 975 
 976   strcpy(timetzval[0].tzabbr, "GMT-0800");
 977   strcpy(timetzval[1].tzabbr, "GMT+0200");
 978   strcpy(timetzval[2].tzabbr, "GMT-0300");
 979 
 980   printf("\nDate & Time types: (%s '%s')\n",
 981     format ? "binary" : "text", datestyle);
 982 
 983   r = PQputf(param, "%date %date %date %time %time %time %timetz %timetz "
 984     "%timetz %timestamp %timestamp %timestamp %interval %timestamptz",
 985     &dateval[0], &dateval[1], &dateval[2], &timeval[0], &timeval[1],
 986     &timeval[2], &timetzval[0], &timetzval[1], &timetzval[2],
 987     &timestampval[0], &timestampval[1], &timestampval[2], &intervalval,
 988     &timestamptzval);
 989   PUTOKAY(param, r, "PQputf(datetime)");
 990 
 991   DROP_TABLE("libpq_datetime");
 992 
 993   result = PQexec(conn, "CREATE TABLE libpq_datetime ("
 994     "date_a date, date_b date, date_c date, "
 995     "time_a time, time_b time, time_c time, "
 996     "timetz_a timetz, timetz_b timetz, timetz_c timetz, "
 997     "timestamp_a timestamp, timestamp_b timestamp, timestamp_c timestamp, "
 998     "intvl interval, tstz timestamptz)");
 999   CMDOKAY("creating libpq_datetime table");
1000   PQclear(result);
1001 
1002   result = PQparamExec(conn, param, "INSERT INTO libpq_datetime VALUES"
1003     "($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)", format);
1004   CMDOKAY("PQparamExec(INSERT:datetime)");
1005   PQclear(result);
1006 
1007   result = PQparamExec(conn, NULL, "SELECT * FROM libpq_datetime", format);
1008   TUPSOKAY("PQparamExec(SELECT:datetime)");
1009 
1010   r = PQgetf(result, 0, "%date %date %date %time %time %time %timetz "
1011     "%timetz %timetz %timestamp %timestamp %timestamp %interval "
1012     "%timestamptz",
1013      0, &date[0],       1, &date[1],       2, &date[2],
1014      3, &tim[0],        4, &tim[1],        5, &tim[2],
1015      6, &timetz[0],     7, &timetz[1],     8, &timetz[2],
1016      9, &timestamp[0], 10, &timestamp[1], 11, &timestamp[2],
1017     12, &interval,     13, &timestamptz);
1018   GETOKAY(r, "PQgetf(datetime)");
1019   PQclear(result);
1020 
1021   for (r=0; r < 3; r++)
1022   {
1023     testcnt++;
1024     if (memcmp(&date[r], &dateval[r], sizeof(PGdate)) == 0)
1025       printf("  %%date - #%d passed\n", r+1);
1026     else
1027     {
1028       failcnt++;
1029       fprintf(stderr, "  %%date - #%d FAILED\n", r+1);
1030     }
1031   }
1032 
1033   for (r=0; r < 3; r++)
1034   {
1035     testcnt++;
1036     if (memcmp(&tim[r], &timeval[r], sizeof(PGtime)) == 0)
1037       printf("  %%time - #%d passed\n", r+1);
1038     else
1039     {
1040       failcnt++;
1041       fprintf(stderr, "  %%time - #%d FAILED\n", r+1);
1042     }
1043   }
1044 
1045   for (r=0; r < 3; r++)
1046   {
1047     testcnt++;
1048     if (memcmp(&timetz[r], &timetzval[r], sizeof(PGtime)) == 0)
1049       printf("  %%timetz - #%d passed\n", r+1);
1050     else
1051     {
1052       failcnt++;
1053       fprintf(stderr, "  %%timetz - #%d FAILED\n", r+1);
1054     }
1055   }
1056 
1057   for (r=0; r < 3; r++)
1058   {
1059 
1060     testcnt++;
1061     if (memcmp(&timestamp[r], &timestampval[r], sizeof(PGtimestamp)) == 0)
1062     {
1063       printf("  %%timestamp - #%d passed\n", r+1);
1064     }
1065     /* Windows may hit rounding issues with usecs.  In this test, usecs of
1066      * 7214 were coming back as 7215.
1067      */
1068 #if defined(_WIN32) || defined(_WIN64)
1069     else if (timestamp[r].time.usec+1 == timestampval[r].time.usec ||
1070        timestamp[r].time.usec-1 == timestampval[r].time.usec)
1071     {
1072       printf("  %%timestamp - #%d passed (warning: known usec rounding "
1073         "issue - sent=%d, recv=%d)\n", r+1,
1074         timestampval[r].time.usec, timestamp[r].time.usec);
1075     }
1076 #endif
1077     else
1078     {
1079       failcnt++;
1080       fprintf(stderr, "  %%timestamp - #%d FAILED\n", r+1);
1081     }
1082   }
1083 
1084   /* interval usecs won't match when using a non-ISO datastyle because only
1085    * 2 fractional digits are provided: 23.291876 => 23.29 secs.
1086    */
1087   if (format == 0 && !strstr(datestyle, "ISO"))
1088   {
1089     printf("  **NOTE: non-ISO DateStyle interval text has a 2 digit limit "
1090       "on the microsecond value ... adjusting\n    => sent=%d, "
1091       "received=%d\n", intervalval.usecs, interval.usecs);
1092     interval.usecs = intervalval.usecs;
1093   }
1094 
1095   testcnt++;
1096   if (memcmp(&interval, &intervalval, sizeof(PGinterval)) == 0)
1097     printf("  %%interval - passed\n");
1098   else
1099   {
1100     failcnt++;
1101     fprintf(stderr, "  %%interval - FAILED\n");
1102 
1103     printf("    SENT: yr=%d,mon=%d,days=%d,hr=%d,min=%d,sec=%d,usec=%d\n",
1104       intervalval.years, intervalval.mons, intervalval.days,
1105       intervalval.hours, intervalval.mins, intervalval.secs,
1106       intervalval.usecs);
1107     printf("    RECV: yr=%d,mon=%d,days=%d,hr=%d,min=%d,sec=%d,usec=%d\n",
1108       interval.years, interval.mons, interval.days,
1109       interval.hours, interval.mins, interval.secs, interval.usecs);
1110   }
1111 
1112   testcnt++;
1113 
1114   if (timestamptz.epoch == timestamptzval.epoch)
1115     printf("  %%timestamptz - passed\n");
1116   else
1117   {
1118     failcnt++;
1119     fprintf(stderr, "  %%timestamptz - FAILED\n");
1120 
1121     printf("SENT: epoch=%lld,isbc=%d,yr=%d,mon=%d,mday=%d,jday=%d,"
1122       "yday=%d,wday=%d,hr=%d,min=%d,sec=%d,usec=%d,withtz=%d,"
1123       "isdst=%d,tzabbr=%s,gmtoff=%d\n",
1124       timestamptzval.epoch,
1125       timestamptzval.date.isbc,timestamptzval.date.year,
1126         timestamptzval.date.mon,
1127       timestamptzval.date.mday,timestamptzval.date.jday,
1128         timestamptzval.date.yday,
1129       timestamptzval.date.wday,
1130       timestamptzval.time.hour, timestamptzval.time.min,
1131         timestamptzval.time.sec,
1132       timestamptzval.time.usec, timestamptzval.time.withtz,
1133         timestamptzval.time.isdst,
1134       timestamptzval.time.tzabbr, timestamptzval.time.gmtoff);
1135 
1136     printf("RECV: epoch=%lld,isbc=%d,yr=%d,mon=%d,mday=%d,jday=%d,"
1137       "yday=%d,wday=%d,hr=%d,min=%d,sec=%d,usec=%d,withtz=%d,isdst=%d,"
1138       "tzabbr=%s,gmtoff=%d\n",
1139       timestamptz.epoch,
1140       timestamptz.date.isbc,timestamptz.date.year,timestamptz.date.mon,
1141       timestamptz.date.mday,timestamptz.date.jday,timestamptz.date.yday,
1142       timestamptz.date.wday,
1143       timestamptz.time.hour, timestamptz.time.min, timestamptz.time.sec,
1144       timestamptz.time.usec, timestamptz.time.withtz, timestamptz.time.isdst,
1145       timestamptz.time.tzabbr, timestamptz.time.gmtoff);
1146   }
1147 }
1148 
1149 static void test_network(int format)
1150 {
1151   int r;
1152   PGinet ipv4, ipv4out;
1153   PGinet ipv6, ipv6out;
1154   PGmacaddr mac = {1,2,3,225,226,227};
1155   PGmacaddr macout;
1156   int have_ipv4=1;
1157   int have_ipv6=1;
1158 
1159 #if defined(_MSC_VER) || defined(__MINGW32__) || defined(HAVE_GETADDRINFO)
1160   struct addrinfo *info = NULL;
1161   struct addrinfo hints = {0};
1162 
1163   memset(&ipv4, 0, sizeof(PGinet));
1164   memset(&ipv4out, 0, sizeof(PGinet));
1165   memset(&ipv6, 0, sizeof(PGinet));
1166   memset(&ipv6out, 0, sizeof(PGinet));
1167 
1168   PQparamReset(param);
1169   printf("\nNetwork types: (%s)\n", format ? "binary" : "text");
1170 
1171   /* this should always work */
1172   if ((r = getaddrinfo("192.168.123.77", NULL, NULL, &info)))
1173   {
1174     have_ipv4 = 0;
1175     //testcnt++;
1176     //failcnt++;
1177     fprintf(stderr, "  IPv4: SKIPPING TEST (no support)\n");
1178   }
1179   else
1180   {
1181     ipv4.mask = 32;
1182     ipv4.sa_buf_len = (int)info->ai_addrlen;
1183     ipv4.is_cidr = 0;
1184     memcpy(ipv4.sa_buf, info->ai_addr, ipv4.sa_buf_len);
1185     memset(ipv4.sa_buf, 0, 2);
1186     ((struct sockaddr *) ipv4.sa_buf)->sa_family = AF_INET;
1187     freeaddrinfo(info);
1188   }
1189 
1190 #ifdef AF_INET6
1191   hints.ai_family = AF_INET6;
1192 #endif
1193 
1194   /* windows may choke on an IPv6 address */
1195   if ((r = getaddrinfo("2001:4f8:3:ba:2e0:81ff:fe22:d1f1",
1196     NULL, &hints, &info)))
1197   {
1198     have_ipv6 = 0;
1199     //testcnt++;
1200     //failcnt++;
1201     fprintf(stderr, "  IPv6: SKIPPING TEST (no support - [%d] %s)\n",
1202       r, gai_strerror(r));
1203   }
1204   else
1205   {
1206     ipv6.mask = 32;
1207     ipv6.sa_buf_len = (int)info->ai_addrlen;
1208     ipv6.is_cidr = 0;
1209     memcpy(ipv6.sa_buf, info->ai_addr, ipv6.sa_buf_len);
1210 
1211 #ifdef AF_INET6
1212     memset(ipv6.sa_buf, 0, 2);
1213     ((struct sockaddr *) ipv6.sa_buf)->sa_family = AF_INET6;
1214 #endif
1215 
1216     freeaddrinfo(info);
1217   }
1218 
1219   r = have_ipv4 ? PQputf(param, "%inet", &ipv4) : PQputf(param, "%null");
1220   PUTOKAY(param, r, "PQputf(network-ipv4)");
1221 
1222   r = have_ipv6 ? PQputf(param, "%inet", &ipv6) : PQputf(param, "%null");
1223   PUTOKAY(param, r, "PQputf(network-ipv6)");
1224 
1225   r = PQputf(param, "%macaddr", &mac);
1226   PUTOKAY(param, r, "PQputf(network-macaddr)");
1227 
1228   DROP_TABLE("libpq_network");
1229 
1230   result = PQexec(conn, "CREATE TABLE libpq_network ("
1231     "ipv4 inet, ipv6 inet, mac macaddr)");
1232   CMDOKAY("creating libpq_network table");
1233   PQclear(result);
1234 
1235   result = PQparamExec(conn, param,
1236     "INSERT INTO libpq_network VALUES ($1,$2,$3)", format);
1237   CMDOKAY("PQparamExec(INSERT:network)");
1238   PQclear(result);
1239 
1240   result = PQparamExec(conn, NULL, "SELECT * FROM libpq_network", format);
1241   TUPSOKAY("PQparamExec(SELECT:network)");
1242 
1243   r = PQgetf(result, 0, "%inet %inet %macaddr",
1244     0, &ipv4out, 1, &ipv6out, 2, &macout);
1245   GETOKAY(r, "PQgetf(network)");
1246 
1247   PQclear(result);
1248 
1249   if (have_ipv4)
1250   {
1251     if (memcmp(&ipv4out, &ipv4, sizeof(PGinet)))
1252     {
1253       failcnt++;
1254       fprintf(stderr, "  %%inet - IPv4 FAILED\n");
1255     }
1256     else
1257       printf("  %%inet - IPv4 passed\n");
1258   }
1259 
1260   if (have_ipv6)
1261   {
1262     if (memcmp(&ipv6out, &ipv6, sizeof(PGinet)))
1263     {
1264       failcnt++;
1265       fprintf(stderr, "  %%inet - IPv6 FAILED\n");
1266     }
1267     else
1268       printf("  %%inet - IPv6 passed\n");
1269   }
1270 
1271   testcnt++;
1272   if (memcmp(&macout, &mac, sizeof(PGmacaddr)))
1273   {
1274     failcnt++;
1275     fprintf(stderr, "  %%macaddr - FAILED\n");
1276   }
1277   else
1278     printf("  %%macaddr - passed\n");
1279 #else
1280   printf("\nNetwork types: (%s) - skipping, no getaddrinfo()\n",
1281     format ? "binary" : "text");
1282 #endif
1283 }
1284 
1285 /* PGtypeProc put for epoch sub-class */
1286 static int epoch_put(PGtypeArgs *args)
1287 {
1288   PGtimestamp ts;
1289   time_t t = va_arg(args->ap, time_t);
1290   struct tm *tm = localtime(&t);
1291 
1292   printf("  epoch_put('%ld')\n", t);
1293 
1294   ts.date.isbc = 0;
1295   ts.date.year = tm->tm_year + 1900; /* PGtimestamp requires 4-digit year */
1296   ts.date.mon  = tm->tm_mon;
1297   ts.date.mday = tm->tm_mday;
1298   ts.time.hour = tm->tm_hour;
1299   ts.time.min  = tm->tm_min;
1300   ts.time.sec  = tm->tm_sec;
1301   ts.time.usec = 0;
1302 
1303   PQlocalTZInfo(&t, &ts.time.gmtoff, &ts.time.isdst, NULL);
1304   return args->super(args, &ts);
1305 }
1306 
1307 /* PGtypeProc get for epoch sub-class */
1308 static int epoch_get(PGtypeArgs *args)
1309 {
1310   PGtimestamp ts;
1311   time_t *t = va_arg(args->ap, time_t *);
1312 
1313   printf("  epoch_get(%s)\n", args->format==0 ? "text" : "binary");
1314 
1315   if (args->super(args, &ts) == -1)
1316     return -1; /* args->errorf called by super class */
1317 
1318   /* since PGtimestamp contains an epoch member as an int8, we can
1319    * just copy that value, rather than doing a mktime().  If time_t
1320    * is 32-bits, the below has a limited range compared to an int8.
1321    * Although, this will work fine for timestamps in a 32-bit time_t range.
1322    * 64-bit time_t should always work.
1323    */
1324   *t = (time_t)ts.epoch;
1325   return 0;
1326 }
1327 
1328 static void test_subclass(int format)
1329 {
1330   int r;
1331   PGparam *prm;
1332   time_t timeout[2] = {0};
1333   /* 1970-01-01 00:00:00, 2000-01-01 00:00:00 */
1334   time_t timevals[] = {0, 946702800};
1335 
1336   printf("\nSub-class: (%s)\n", format ? "binary" : "text");
1337 
1338   DROP_TABLE("libpq_subclass");
1339   result = PQexec(conn,
1340     "CREATE TABLE libpq_subclass (n timestamptz, a timestamptz)");
1341   CMDOKAY("creating libpq_subclass table");
1342   PQclear(result);
1343 
1344   prm = PQparamCreate(conn);
1345   if (!PQputf(prm, "%epoch %epoch", timevals[0], timevals[1]))
1346     fprintf(stderr, "subclass_put failed: %s\n", PQgeterror());
1347   result = PQparamExec(conn, prm, "INSERT INTO libpq_subclass "
1348     "VALUES ($1, $2)", format);
1349   PQparamClear(prm);
1350   CMDOKAY("PQparamExec(INSERT:subclass)");
1351   PQclear(result);
1352 
1353   result = PQparamExec(conn, NULL, "SELECT * FROM libpq_subclass", format);
1354   TUPSOKAY("PQparamExec(SELECT:subclass)");
1355 
1356   /* use schema-qualified name */
1357   r = PQgetf(result, 0, "%libpq.epoch %epoch",
1358     0, &timeout[0], 1, &timeout[1]);
1359   GETOKAY(r, "PQgetf(subclass)");
1360   PQclear(result);
1361 
1362   testcnt++;
1363   if (memcmp(timeout, timevals, sizeof(timeout)))
1364   {
1365     failcnt++;
1366     fprintf(stderr, "  %%epoch - FAILED (putval=%ld,%ld, getval=%ld,%ld)\n",
1367       timevals[0], timevals[1], timeout[0], timeout[1]);
1368   }
1369   else
1370     fprintf(stderr, "  %%epoch - passed\n");
1371 }