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