libpqtypes - handler.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 /*
   3  * handler.c
   4  *   Type handler management functions, the core of the handler system.
   5  *
   6  * Copyright (c) 2011 eSilo, LLC. All rights reserved.
   7  * This is free software; see the source for copying conditions.  There is
   8  * NO warranty; not even for MERCHANTABILITY or  FITNESS FOR A  PARTICULAR
   9  * PURPOSE.
  10  */
  11 
  12 #include "libpqtypes-int.h"
  13 
  14 /* -------------------------
  15  * Built-in handler defaults:
  16  *   base_id, nattrs, freeAttDescs, attDescsBuf, attDescs
  17  */
  18 #define __HANDLER_DEFAULTS__ -1, 0, 0, {{0}}, NULL
  19 
  20 /* id, schema, typname, typlen, oid, arroid, typput, typget, defaults */
  21 static PGtypeHandler pg_handlers[] = {
  22   /* character types */
  23   {0, "pg_catalog", "char", 1, CHAROID, 1002, pqt_put_char,
  24     pqt_get_char, __HANDLER_DEFAULTS__},
  25   {1, "pg_catalog", "varchar", -1, VARCHAROID, 1015, pqt_put_text,
  26     pqt_get_text, __HANDLER_DEFAULTS__}, /* supports ptr */
  27   {2, "pg_catalog", "bpchar", -1, BPCHAROID, 1014, pqt_put_text,
  28     pqt_get_text, __HANDLER_DEFAULTS__}, /* supports ptr */
  29   {3, "pg_catalog", "text", -1, TEXTOID, 1009, pqt_put_text,
  30     pqt_get_text, __HANDLER_DEFAULTS__}, /* supports ptr */
  31 
  32   /* boolean types */
  33   {4, "pg_catalog", "bool", 1, BOOLOID, 1000, pqt_put_bool,
  34     pqt_get_bool, __HANDLER_DEFAULTS__},
  35 
  36   /* numeric types */
  37   {5, "pg_catalog", "int2", 2, INT2OID, 1005, pqt_put_int2,
  38     pqt_get_int2, __HANDLER_DEFAULTS__},
  39   {6, "pg_catalog", "int4", 4, INT4OID, 1007, pqt_put_int4,
  40     pqt_get_int4, __HANDLER_DEFAULTS__},
  41   {7, "pg_catalog", "int8", 8, INT8OID, 1016, pqt_put_int8,
  42     pqt_get_int8, __HANDLER_DEFAULTS__},
  43   {8, "pg_catalog", "float4", 4, FLOAT4OID, 1021, pqt_put_float4,
  44     pqt_get_float4, __HANDLER_DEFAULTS__},
  45   {9, "pg_catalog", "float8", 8, FLOAT8OID, 1022, pqt_put_float8,
  46     pqt_get_float8, __HANDLER_DEFAULTS__},
  47   {10, "pg_catalog", "numeric", -1, NUMERICOID, 1231, pqt_put_numeric,
  48     pqt_get_numeric, __HANDLER_DEFAULTS__},
  49 
  50   /* bytea types */
  51   {11, "pg_catalog", "bytea", -1, BYTEAOID, 1001, pqt_put_bytea,
  52     pqt_get_bytea, __HANDLER_DEFAULTS__}, /* supports ptr */
  53 
  54   /* geometric types */
  55   {12, "pg_catalog", "point", 16, POINTOID, 1017, pqt_put_point,
  56     pqt_get_point, __HANDLER_DEFAULTS__},
  57   {13, "pg_catalog", "lseg", 32, LSEGOID, 1018, pqt_put_lseg,
  58     pqt_get_lseg, __HANDLER_DEFAULTS__},
  59   {14, "pg_catalog", "box", 32, BOXOID, 1020, pqt_put_box,
  60     pqt_get_box, __HANDLER_DEFAULTS__},
  61   {15, "pg_catalog", "circle", 24, CIRCLEOID, 719, pqt_put_circle,
  62     pqt_get_circle, __HANDLER_DEFAULTS__},
  63   {16, "pg_catalog", "path", -1, PATHOID, 1019, pqt_put_path,
  64     pqt_get_path, __HANDLER_DEFAULTS__},
  65   {17, "pg_catalog", "polygon", -1, POLYGONOID, 1027, pqt_put_polygon,
  66     pqt_get_polygon, __HANDLER_DEFAULTS__},
  67 
  68   /* monetary types */
  69   {18, "pg_catalog", "money", 8, CASHOID, 791, pqt_put_money,
  70     pqt_get_money, __HANDLER_DEFAULTS__},
  71 
  72   /* network address typess */
  73   {19, "pg_catalog", "inet", -1, INETOID, 1041, pqt_put_inet,
  74     pqt_get_inet, __HANDLER_DEFAULTS__},
  75   {20, "pg_catalog", "cidr", -1, CIDROID, 651, pqt_put_inet,
  76     pqt_get_cidr, __HANDLER_DEFAULTS__},
  77   {21, "pg_catalog", "macaddr", 6, MACADDROID, 1040, pqt_put_macaddr,
  78     pqt_get_macaddr, __HANDLER_DEFAULTS__},
  79 
  80   /* date & time types */
  81   {22, "pg_catalog", "time", 8, TIMEOID, 1183, pqt_put_time,
  82     pqt_get_time, __HANDLER_DEFAULTS__},
  83   {23, "pg_catalog", "timetz", 12, TIMETZOID, 1270, pqt_put_timetz,
  84     pqt_get_timetz, __HANDLER_DEFAULTS__},
  85   {24, "pg_catalog", "date", 4, DATEOID, 1182, pqt_put_date,
  86     pqt_get_date, __HANDLER_DEFAULTS__},
  87   {25, "pg_catalog", "timestamp", 8, TIMESTAMPOID, 1115, pqt_put_timestamp,
  88     pqt_get_timestamp, __HANDLER_DEFAULTS__},
  89   {26, "pg_catalog", "timestamptz", 8, TIMESTAMPTZOID, 1185,
  90     pqt_put_timestamptz, pqt_get_timestamptz, __HANDLER_DEFAULTS__},
  91   {27, "pg_catalog", "interval", 16, INTERVALOID, 1187, pqt_put_interval,
  92     pqt_get_interval, __HANDLER_DEFAULTS__},
  93 
  94   /* object identifier types */
  95   {28, "pg_catalog", "oid", 4, OIDOID, 1028, pqt_put_int4,
  96     pqt_get_int4, __HANDLER_DEFAULTS__},
  97 
  98   /* UUID types */
  99   {29, "pg_catalog", "uuid", 16, UUIDOID, 2951, pqt_put_uuid,
 100     pqt_get_uuid, __HANDLER_DEFAULTS__},
 101 
 102   /* Record types (composites) */
 103   {30, "pg_catalog", "record", -1, RECORDOID, 0, pqt_put_record,
 104     pqt_get_record, __HANDLER_DEFAULTS__},
 105 
 106   /* pqt types */
 107   {31, "pqt", "str", -1, InvalidOid, 0, pqt_put_str,
 108     NULL, __HANDLER_DEFAULTS__}, /* supports ptr */
 109   {32, "pqt", "null", -1, InvalidOid, 0, pqt_put_null,
 110     NULL, __HANDLER_DEFAULTS__},
 111 
 112   /* more character types */
 113   {33, "pg_catalog", "name", -1, NAMEOID, 1003, pqt_put_text,
 114     pqt_get_text, __HANDLER_DEFAULTS__} /* supports ptr */
 115 };
 116 
 117 static int
 118 expandHandlers(PGtypeData *typeData);
 119 
 120 static PGrecordAttDesc *
 121 initAttDescs(PGtypeHandler *h, char *attrs);
 122 
 123 static char *
 124 parseType(const char *spec, char *schema, char *typname, int typpos);
 125 
 126 static int
 127 getTypeParams(PGconn *conn, PGregisterType *types, int count,
 128   PGarray *names, PGarray *schemas);
 129 
 130 static int
 131 checkTypeLookups(PGresult *res, PGregisterType *types, int count);
 132 
 133 /* If res is NULL, non-blocking send is used, otherwise a blocking
 134  * exec is issued and *res contains the result.  Returns zero on
 135  * error and non-zero on success.
 136  */
 137 static int
 138 performRegisterQuery(PGconn *conn, int which, PGregisterType *types,
 139   int count, PGresult **res);
 140 
 141 /* Called by PQregisterTypes() for each type provided. */
 142 static int
 143 registerSubClass(PGtypeData *connData, const char *type_name,
 144   PGtypeProc typput, PGtypeProc typget);
 145 
 146 int
 147 PQinitTypes(PGconn *conn)
 148 {
 149   return PQregisterEventProc(conn, pqt_eventproc, "pqtypes", NULL);
 150 }
 151 
 152 /* Deprecated */
 153 int
 154 PQregisterSubClasses(PGconn *conn, PGregisterType *types, int count)
 155 {
 156   return PQregisterTypes(conn, PQT_SUBCLASS, types, count, 0);
 157 }
 158 
 159 /* Deprecated */
 160 int
 161 PQregisterUserDefinedTypes(PGconn *conn, PGregisterType *types, int count)
 162 {
 163   return PQregisterTypes(conn, PQT_USERDEFINED, types, count, 0);
 164 }
 165 
 166 /* Deprecated */
 167 int
 168 PQregisterComposites(PGconn *conn, PGregisterType *types, int count)
 169 {
 170   return PQregisterTypes(conn, PQT_COMPOSITE, types, count, 0);
 171 }
 172 
 173 int
 174 PQregisterTypes(PGconn *conn, int which, PGregisterType *types,
 175   int count, int async)
 176 {
 177   int n = FALSE;
 178 
 179   PQseterror(NULL);
 180 
 181   if (!conn)
 182   {
 183     PQseterror("PGconn cannot be NULL");
 184     return FALSE;
 185   }
 186 
 187   if (!types)
 188   {
 189     PQseterror("PGregisterType[] cannot be NULL");
 190     return FALSE;
 191   }
 192 
 193   if (count < 0)
 194   {
 195     PQseterror("PGregisterType[] count cannot be less than zero");
 196     return FALSE;
 197   }
 198 
 199   /* nothing to do, silently ignore it */
 200   if (count == 0)
 201     return TRUE;
 202 
 203   if (which == PQT_SUBCLASS)
 204   {
 205     int i;
 206     PGtypeData *connData;
 207 
 208     if (!(connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc)))
 209     {
 210       PQseterror("PGconn is missing event data");
 211       return FALSE;
 212     }
 213 
 214     for (i=0; i < count; i++)
 215     {
 216       n = registerSubClass(connData, types[i].typname,
 217         types[i].typput, types[i].typget);
 218 
 219       if (!n)
 220         return FALSE;
 221     }
 222   }
 223   else
 224   {
 225     PGresult *res = NULL;
 226 
 227     n = performRegisterQuery(conn, which, types, count, async ? NULL : &res);
 228 
 229     /* If not async, register the result and clear it. */
 230     if (n && !async)
 231     {
 232       n = PQregisterResult(conn, which, types, count, res);
 233       PQclear(res);
 234     }
 235   }
 236 
 237   return n;
 238 }
 239 
 240 int
 241 PQregisterResult(PGconn *conn, int which, PGregisterType *types,
 242   int count, PGresult *res)
 243 {
 244   int i;
 245   PGtypeData *connData;
 246   char typname[PQT_MAXIDLEN + 1];
 247   char typschema[PQT_MAXIDLEN + 1];
 248   /* inherit typput and typget from record type */
 249   PGtypeHandler *h_rec = pqt_gethandler(NULL, 0, "pg_catalog", "record");
 250 
 251   PQseterror(NULL);
 252 
 253   if (!conn)
 254   {
 255     PQseterror("PGconn cannot be NULL");
 256     return FALSE;
 257   }
 258 
 259   if (!res)
 260   {
 261     PQseterror("PGresult cannot be NULL");
 262     return FALSE;
 263   }
 264 
 265   if (which == PQT_SUBCLASS)
 266   {
 267     PQseterror("Cannot call PQregisterResult for a subclass registration.");
 268     return FALSE;
 269   }
 270 
 271   if (!(connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc)))
 272   {
 273     PQseterror("PGconn is missing event data");
 274     return FALSE;
 275   }
 276 
 277   if (!types)
 278   {
 279     PQseterror("PGregisterType[] cannot be NULL");
 280     return FALSE;
 281   }
 282 
 283   if (count < 0)
 284   {
 285     PQseterror("PGregisterType[] count cannot be less than zero");
 286     return FALSE;
 287   }
 288 
 289   if(!checkTypeLookups(res, types, count))
 290     return FALSE;
 291 
 292   for (i=0; i < PQntuples(res); i++)
 293   {
 294     int flags;
 295     PGint2 typlen;
 296     PGtypeHandler *h;
 297 
 298     if (which == PQT_USERDEFINED && !types[i].typput && !types[i].typget)
 299     {
 300       PQseterror("Must provide a put and/or a get routine: '%s'",
 301         types[i].typname);
 302       return FALSE;
 303     }
 304 
 305     /* make sure conn's type handlers array is large enough */
 306     if (!expandHandlers(connData))
 307       return FALSE;
 308 
 309     /* create the handler */
 310     h = &connData->typhandlers[connData->typhcnt];
 311     memset(h, 0, sizeof(PGtypeHandler));
 312 
 313     if (!PQgetf(res, i, "%oid %oid %int2", 1, &h->typoid,
 314       2, &h->typoid_array, 3, &typlen))
 315     {
 316       return FALSE;
 317     }
 318 
 319     h->id = connData->typhcnt + countof(pg_handlers);
 320     h->typlen = (int) typlen;
 321     h->base_id = -1;
 322 
 323     if (which == PQT_USERDEFINED)
 324     {
 325       h->typput = types[i].typput;
 326       h->typget = types[i].typget;
 327     }
 328     else
 329     {
 330       h->typput = h_rec->typput;
 331       h->typget = h_rec->typget;
 332     }
 333 
 334     /* parse out type and schema names again */
 335     (void ) pqt_parsetype(types[i].typname, typschema, typname, &flags, 1);
 336     pqt_strcpy(h->typschema, sizeof(h->typschema), typschema);
 337     pqt_strcpy(h->typname, sizeof(h->typname), typname);
 338 
 339     /* Process composite attributes */
 340     if(which == PQT_COMPOSITE)
 341     {
 342       PGtext attrs;
 343       int nattrs;
 344       PGrecordAttDesc *attDescs;
 345 
 346       if (!PQgetf(res, i, "%text", 4, &attrs))
 347       {
 348         return FALSE;
 349       }
 350 
 351       if (!(attDescs = initAttDescs(h, attrs)))
 352         return FALSE;
 353 
 354       for (nattrs=0; *attrs; nattrs++)
 355       {
 356         char *p;
 357         char *name;
 358         int len;
 359 
 360         /* Attribute Text Encoding:
 361          *   "attoid,attlen,atttypmod,name_hex attoid,etc..."
 362          */
 363 
 364         attDescs[nattrs].attoid    = (int) strtol(attrs, &attrs, 10);
 365         attDescs[nattrs].attlen    = (int) strtol(attrs + 1, &attrs, 10);
 366         attDescs[nattrs].atttypmod = (int) strtol(attrs + 1, &attrs, 10);
 367 
 368         /* skip comma before name */
 369         attrs++;
 370 
 371         /* attribute name in hex */
 372         if (!(p = strchr(attrs, ' ')))
 373           p = attrs + strlen(attrs); /* last attr, point at NUL */
 374 
 375         /* truncate name if it exceeds buffer */
 376         len = (int) (p - attrs);
 377         if (len >= (int) sizeof(attDescs[nattrs].attname))
 378           len = (int) (sizeof(attDescs[nattrs].attname) - 1);
 379 
 380         /* hex decode and copy */
 381         for (name = attDescs[nattrs].attname; attrs < p; attrs += 2)
 382           *name++ = (char) (pqt_hex_to_dec(attrs[0]) << 4)
 383             | pqt_hex_to_dec(attrs[1]);
 384         *name = 0;
 385       }
 386 
 387       h->nattrs = nattrs;
 388       h->attDescs = attDescs;
 389     }
 390 
 391     connData->typhcnt++;
 392   }
 393 
 394   return TRUE;
 395 }
 396 
 397 int
 398 PQclearTypes(PGconn *conn)
 399 {
 400   PGtypeData *connData;
 401 
 402   if (!conn)
 403   {
 404     PQseterror("PGconn cannot be NULL");
 405     return FALSE;
 406   }
 407 
 408   if (!(connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc)))
 409   {
 410     PQseterror("PGconn is missing event data");
 411     return FALSE;
 412   }
 413 
 414   pqt_cleartypes(connData);
 415 
 416   return TRUE;
 417 }
 418 
 419 /* Do not call when hcnt is 0.  This returns NULL when malloc fails.
 420  * Passing in 0 could only do the same thing (ambiguos), so it is simply
 421  * not handled and may dump core.
 422  */
 423 PGtypeHandler *
 424 pqt_duphandlers(PGtypeHandler *handlers, int hcnt)
 425 {
 426   int i;
 427   PGtypeHandler *h = (PGtypeHandler *) malloc(hcnt * sizeof(PGtypeHandler));
 428 
 429   if (!h)
 430     return NULL;
 431 
 432   /* In the most common cases, this is the total cost of the dup.
 433    * Previously, the handler had 4 inner strings that required a deep
 434    * copy.  Surprisingly, this had a noticeable overhead.  This was
 435    * solved by using fixed length buffers in the type handler struct.
 436    * Also, a fixed length attDescs buffer was added to avoid having
 437    * to perform a deep copy for the common cases, 16 or less attrs.
 438    */
 439   memcpy(h, handlers, hcnt * sizeof(PGtypeHandler));
 440 
 441   /* Possibly deep copy PGrecordAttDesc array.  Otherwise, assign it
 442    * to the fixed length buffer.
 443    */
 444   for (i=0; i < hcnt; i++)
 445   {
 446     if (h[i].nattrs == 0)
 447       continue;
 448 
 449     /* There are attributes but the attDescs buffer can be used rather
 450      * than allocating and copying.  The data was copied during the
 451      * handlers memcpy prior to this loop.
 452      */
 453     if (!h[i].freeAttDescs)
 454     {
 455       h[i].attDescs = h[i].attDescsBuf;
 456       continue;
 457     }
 458 
 459     /* ------------------------------
 460      * Must allocate and copy.
 461      */
 462 
 463     h[i].attDescs = (PGrecordAttDesc *) malloc(
 464       h[i].nattrs * sizeof(PGrecordAttDesc));
 465 
 466     if (!h[i].attDescs)
 467     {
 468       pqt_freehandlers(h, i+1);
 469       return NULL;
 470     }
 471 
 472     memcpy(h[i].attDescs, handlers[i].attDescs,
 473       h[i].nattrs * sizeof(PGrecordAttDesc));
 474   }
 475 
 476   return h;
 477 }
 478 
 479 void
 480 pqt_cleartypes(PGtypeData *typeData)
 481 {
 482   if (typeData)
 483   {
 484     pqt_freehandlers(typeData->typhandlers, typeData->typhcnt);
 485 
 486     typeData->typhandlers = NULL;
 487     typeData->typhcnt = 0;
 488     typeData->typhmax = 0;
 489   }
 490 }
 491 
 492 void
 493 pqt_freehandlers(PGtypeHandler *handlers, int hcnt)
 494 {
 495   int i;
 496 
 497   /* Free attDescs */
 498   for (i=0; i < hcnt; i++)
 499     if (handlers[i].freeAttDescs && handlers[i].attDescs)
 500       free(handlers[i].attDescs);
 501 
 502   if (handlers)
 503     free(handlers);
 504 }
 505 
 506 PGtypeHandler *
 507 pqt_gethandler(PGtypeHandler *handlers, int hcnt,
 508   const char *schema, const char *typname)
 509 {
 510   int i;
 511   int noschema = !schema || !*schema;
 512 
 513   if (!typname || !*typname)
 514     return NULL;
 515 
 516   /* user registered types are searched first */
 517   for (i=0; i < hcnt; i++)
 518   {
 519     if ((noschema || strcmp(handlers[i].typschema, schema)==0) &&
 520        strcmp(handlers[i].typname, typname)==0)
 521     {
 522       return &handlers[i];
 523     }
 524   }
 525 
 526   /* builtin types searched last */
 527   for (i=0; i < countof(pg_handlers); i++)
 528   {
 529     if ((noschema || strcmp(pg_handlers[i].typschema, schema)==0) &&
 530        strcmp(pg_handlers[i].typname, typname)==0)
 531     {
 532       return &pg_handlers[i];
 533     }
 534   }
 535 
 536   return NULL;
 537 }
 538 
 539 PGtypeHandler *
 540 pqt_gethandlerbyid(PGtypeHandler *handlers, int hcnt, int id)
 541 {
 542   if (id <= -1)
 543     return NULL;
 544 
 545   if (id < countof(pg_handlers))
 546     return &pg_handlers[id];
 547 
 548   id -= countof(pg_handlers);
 549   if (id >= hcnt)
 550     return NULL;
 551 
 552   return &handlers[id];
 553 }
 554 
 555 int
 556 pqt_argssuper(PGtypeArgs *args, ...)
 557 {
 558   int r;
 559   va_list ap;
 560   PGtypeHandler *baseclass;
 561   PGtypeHandler *subclass = args->typhandler;
 562   PGtypeData *resData = NULL;
 563 
 564   if (!args->is_put)
 565   {
 566     resData = (PGtypeData *) PQresultInstanceData(
 567       args->get.result, pqt_eventproc);
 568 
 569     if (!resData)
 570       return args->errorf(args, "PGresult is missing event data");
 571   }
 572 
 573   /* should always work, but play it safe */
 574   baseclass = pqt_gethandlerbyid(
 575     args->is_put ? args->put.param->typhandlers : resData->typhandlers,
 576     args->is_put ? args->put.param->typhcnt : resData->typhcnt,
 577     subclass->base_id);
 578 
 579   if (!baseclass)
 580     return args->errorf(args, "type handler has no base type");
 581 
 582   args->typhandler = baseclass;
 583 
 584   va_copy(ap, args->ap);
 585   va_start(args->ap, args);
 586   r = args->is_put ? baseclass->typput(args) : baseclass->typget(args);
 587   va_copy(args->ap, ap);
 588 
 589   args->typhandler = subclass;
 590   return r;
 591 }
 592 
 593 /* FQTN standards for Fully Qualified Type Name.  Returns a pointer to out.
 594  * Only returns NULL if out is NULL or outl <= 0.
 595  */
 596 char *
 597 pqt_fqtn(char *out, size_t outl, const char *schema, const char *typname)
 598 {
 599   int r;
 600   int have_schema = schema && *schema;
 601 
 602   if (!out || outl<=0)
 603     return NULL;
 604 
 605   *out = 0;
 606   if (!typname || !*typname)
 607     return out;
 608 
 609   r = pqt_snprintf(out, outl, "%s%s%s", have_schema ? schema : "",
 610     have_schema ? "." : "", typname);
 611 
 612   if (r < 0)
 613   {
 614     *out = 0;
 615     return out;
 616   }
 617 
 618   return out;
 619 }
 620 
 621 /* only checks builin types or pqt types.  User registered types must
 622  * be checked by the user's handler functions.
 623  */
 624 int
 625 pqt_allowsptr(PGtypeHandler *h)
 626 {
 627   /* pg_catalog.[bpchar, varchar, name, text, bytea] */
 628   if (strcmp(h->typschema, "pg_catalog")==0)
 629   {
 630     if (strcmp(h->typname, "bpchar") &&
 631        strcmp(h->typname, "varchar") &&
 632        strcmp(h->typname, "text") &&
 633        strcmp(h->typname, "bytea") &&
 634        strcmp(h->typname, "name"))
 635     {
 636       return FALSE;
 637     }
 638   }
 639   /* pqt.str */
 640   else if (strcmp(h->typschema, "pqt")==0 && strcmp(h->typname, "str"))
 641   {
 642     return FALSE;
 643   }
 644 
 645   return TRUE;
 646 }
 647 
 648 void
 649 pqt_getfmtinfo(const PGconn *conn, PGtypeFormatInfo *info)
 650 {
 651   const char *value;
 652 
 653   memset(info, 0, sizeof(PGtypeFormatInfo));
 654 
 655   if ((value = PQparameterStatus(conn, "DateStyle")))
 656     pqt_strcpy(info->datestyle, sizeof(info->datestyle), value);
 657 
 658   if ((value = PQparameterStatus(conn, "integer_datetimes")))
 659     info->integer_datetimes = strcmp(value, "on")==0 ? TRUE : FALSE;
 660 
 661   info->sversion = PQserverVersion(conn);
 662   info->pversion = PQprotocolVersion(conn);
 663 }
 664 
 665 static int registerSubClass(PGtypeData *connData, const char *type_name,
 666   PGtypeProc typput, PGtypeProc typget)
 667 {
 668   char *s;
 669   PGtypeHandler *h_sub;
 670   PGtypeHandler *h_base;
 671   char sub_typschema[PQT_MAXIDLEN + 1];
 672   char sub_typname[PQT_MAXIDLEN + 1];
 673   char base_typschema[PQT_MAXIDLEN + 1];
 674   char base_typname[PQT_MAXIDLEN + 1];
 675   char sub_fqtn[200];
 676   char base_fqtn[200];
 677 
 678   if (!(s = parseType(type_name, sub_typschema, sub_typname, 1)))
 679     return FALSE;
 680 
 681   if (*s != '=')
 682   {
 683     PQseterror("Missing inheritence operator '=': %s", type_name);
 684     return FALSE;
 685   }
 686 
 687   if (!parseType(s + 1, base_typschema, base_typname, 1))
 688     return FALSE;
 689 
 690   /* lookup the base handler */
 691   h_base = pqt_gethandler(connData->typhandlers, connData->typhcnt,
 692     base_typschema, base_typname);
 693 
 694   if (!h_base)
 695   {
 696     PQseterror("typname '%s' does not exist, '%s' cannot sub-class it",
 697       pqt_fqtn(base_fqtn, sizeof(base_fqtn), base_typschema, base_typname),
 698       pqt_fqtn(sub_fqtn, sizeof(sub_fqtn), sub_typschema, sub_typname));
 699     return FALSE;
 700   }
 701 
 702   /* cannot sub-class a record type */
 703   if (h_base->typoid == RECORDOID)
 704   {
 705     PQseterror("Cannot sub-class pg_catalog.record '%s'",
 706       pqt_fqtn(sub_fqtn, sizeof(sub_fqtn), sub_typschema, sub_typname));
 707     return FALSE;
 708   }
 709 
 710   if (!expandHandlers(connData))
 711     return FALSE;
 712 
 713   h_sub = &connData->typhandlers[connData->typhcnt];
 714   memset(h_sub, 0, sizeof(PGtypeHandler));
 715 
 716   h_sub->id = connData->typhcnt + countof(pg_handlers);
 717   h_sub->typlen = h_base->typlen;
 718   h_sub->typoid = h_base->typoid;
 719   h_sub->typoid_array = h_base->typoid_array;
 720   h_sub->typput = typput;
 721   h_sub->typget = typget;
 722   h_sub->base_id = h_base->id;
 723 
 724   pqt_strcpy(h_sub->typschema,
 725     sizeof(h_sub->typschema), sub_typschema);
 726 
 727   pqt_strcpy(h_sub->typname,
 728     sizeof(h_sub->typname), sub_typname);
 729 
 730   connData->typhcnt++;
 731   return TRUE;
 732 }
 733 
 734 static int
 735 expandHandlers(PGtypeData *typeData)
 736 {
 737   int hmax;
 738   PGtypeHandler *h;
 739 
 740   if (typeData->typhcnt < typeData->typhmax)
 741     return TRUE;
 742 
 743   hmax = typeData->typhmax ? (typeData->typhmax * 3) / 2 : 8;
 744   h = (PGtypeHandler *) pqt_realloc(
 745     typeData->typhandlers, sizeof(PGtypeHandler) * hmax);
 746 
 747   if (!h)
 748   {
 749     PQseterror(PQT_OUTOFMEMORY);
 750     return FALSE;
 751   }
 752 
 753   typeData->typhandlers = h;
 754   typeData->typhmax = hmax;
 755   return TRUE;
 756 }
 757 
 758 static int
 759 checkTypeLookups(PGresult *res, PGregisterType *types, int count)
 760 {
 761   int i;
 762   int ntups = PQntuples(res);
 763 
 764   /* The tuple count must match the requested count.  The server omits
 765    * tuples for types it did not find.  For those it did find, it returns
 766    * a sequenctial index.  The first gap found is our missing type.  This
 767    * only reports about the first missing type.
 768    */
 769   if (ntups == count)
 770     return TRUE;
 771 
 772   for (i=0; i < ntups; i++)
 773   {
 774     int idx;
 775 
 776     if (!PQgetf(res, i, "%int4", 0, &idx))
 777       return FALSE;
 778 
 779     /* 'i' should always match idx-1, postgresql arrays are 1-based.
 780      * This is a missing type, first gap in the sequence.
 781      */
 782     if (i != idx-1)
 783       break;
 784   }
 785 
 786   PQseterror("server type lookup failed: could not find '%s'",
 787     types[i].typname);
 788 
 789   return FALSE;
 790 }
 791 
 792 /* This is part of a performance enhancement for getting arrays
 793  * and/or composites.  They require generating PGresults which
 794  * causes pqt_duphandlers() to run.  Its amazing how much a simple
 795  * malloc+memcpy costs after around 10000 or so.  The common case
 796  * avoids this by using a fixed length PGrecordAttDesc buffer.  If
 797  * there are a large number of attributes, the slower path is used.
 798  * Again this is small, maybe 10% of the overall 63% win.
 799  */
 800 static PGrecordAttDesc *initAttDescs(PGtypeHandler *h, char *attrs)
 801 {
 802   char *p;
 803   int nattrs = 1;
 804   PGrecordAttDesc *attDescs;
 805 
 806   for(p = attrs; *p; nattrs++, ++p)
 807     if(!(p = strchr(p, ' ')))
 808       break;
 809 
 810   if (nattrs < (int) (sizeof(h->attDescsBuf) / sizeof(h->attDescsBuf[0])))
 811   {
 812     h->freeAttDescs =  0;
 813     attDescs = h->attDescsBuf;
 814   }
 815   else
 816   {
 817     attDescs = (PGrecordAttDesc *) malloc(nattrs * sizeof(PGrecordAttDesc));
 818     if (!attDescs)
 819     {
 820       PQseterror(PQT_OUTOFMEMORY);
 821       return NULL;
 822     }
 823 
 824     h->freeAttDescs = 1;
 825   }
 826 
 827   return attDescs;
 828 }
 829 
 830 /* wraps pqt_parsetype to toggle out illegal flags during a register */
 831 static char *parseType(const char *spec, char *typschema, char *typname,
 832   int typpos)
 833 {
 834   char *s;
 835   int flags;
 836 
 837   if (!(s = pqt_parsetype(spec, typschema, typname, &flags, typpos)))
 838     return NULL;
 839 
 840   if (flags & TYPFLAG_INVALID)
 841     return NULL;
 842 
 843   if (flags & TYPFLAG_ARRAY)
 844   {
 845     PQseterror("Cannot use an array[] during a type handler registration.");
 846     return NULL;
 847   }
 848 
 849   if (flags & TYPFLAG_POINTER)
 850   {
 851     PQseterror("Cannot use a type* during a type handler registration.");
 852     return NULL;
 853   }
 854 
 855   return s;
 856 }
 857 
 858 static int getTypeParams(PGconn *conn, PGregisterType *types, int count,
 859   PGarray *names, PGarray *schemas)
 860 {
 861   int i;
 862 
 863   names->ndims = 0;
 864   schemas->ndims = 0;
 865 
 866   if (!(names->param = PQparamCreate(conn)))
 867     return FALSE;
 868 
 869   if (!(schemas->param = PQparamCreate(conn)))
 870   {
 871     PQparamClear(names->param);
 872     return FALSE;
 873   }
 874 
 875   for (i=0; i < count; i++)
 876   {
 877     char typname[PQT_MAXIDLEN + 1];
 878     char typschema[PQT_MAXIDLEN + 1];
 879     char *s = parseType(types[i].typname, typschema, typname, 1);
 880 
 881     if (!s)
 882     {
 883       PQparamClear(names->param);
 884       PQparamClear(schemas->param);
 885       return FALSE;
 886     }
 887 
 888     s = *typschema ? typschema : NULL;
 889     if (!PQputf(names->param, "%text", typname) ||
 890       !PQputf(schemas->param, "%text", s))
 891     {
 892       PQparamClear(names->param);
 893       PQparamClear(schemas->param);
 894       return FALSE;
 895     }
 896   }
 897 
 898   return TRUE;
 899 }
 900 
 901 
 902 
 903 /* Lookup types, including composites.  Arguments are:
 904  *   schemas text[], type_names text[], want_attrs bool
 905  */
 906 #define LOOKUP_TYPES \
 907 "WITH nspnames AS" \
 908 "(" \
 909 "  SELECT * FROM information_schema._pg_expandarray(%text[])" \
 910 ")," \
 911 "typnames AS" \
 912 "(" \
 913 "  SELECT * FROM information_schema._pg_expandarray(%text[])" \
 914 ")," \
 915 "curpath AS" \
 916 "(" \
 917 "  SELECT * FROM information_schema._pg_expandarray(current_schemas(true))" \
 918 ")," \
 919 "composites AS" \
 920 "(" \
 921 "  SELECT n.n AS idx, n.x AS nspname, t.x AS typname" \
 922 "    FROM nspnames n LEFT JOIN typnames t ON n.n = t.n" \
 923 "      AND n.x IS NOT NULL" \
 924 "      WHERE t.x IS NOT NULL" \
 925 "  UNION ALL" \
 926 "  SELECT n.n AS idx," \
 927 "  (" \
 928 "    SELECT n.nspname from pg_type nt JOIN pg_namespace n ON " \
 929          "nt.typnamespace = n.oid" \
 930 "      JOIN curpath c ON c.x = n.nspname" \
 931 "      WHERE nt.typname = t.x" \
 932 "      ORDER BY c.n LIMIT 1" \
 933 "  ) AS nspname, t.x AS typname" \
 934 "    FROM nspnames n LEFT JOIN typnames t ON n.n = t.n" \
 935 "      AND n.x IS NULL" \
 936 "      WHERE t.x IS NOT NULL" \
 937 ")" \
 938 "SELECT idx, t.oid AS typoid, a.oid AS arroid, t.typlen," \
 939 " (" \
 940 "   CASE WHEN %bool THEN (" \
 941 "   SELECT array_to_string" \
 942 "   (" \
 943 "     ARRAY" \
 944 "     (" \
 945 "       SELECT CASE WHEN tt.typtype = 'd' THEN tt.typbasetype " \
 946            "ELSE a.atttypid END ||" \
 947 "         ',' || attlen || ',' || atttypmod || ',' || " \
 948             "encode(attname::bytea, 'hex')" \
 949 "         FROM pg_type b" \
 950 "           JOIN pg_attribute a ON b.typrelid = a.attrelid" \
 951 "           JOIN pg_type tt ON a.atttypid = tt.oid" \
 952 "           WHERE b.oid = t.oid" \
 953 "             AND a.attnum > 0" \
 954 "             AND NOT a.attisdropped" \
 955 "           ORDER BY a.attnum" \
 956 "    ), ' ')" \
 957 "  ) ELSE NULL END) AS arr_props" \
 958 "  FROM composites c" \
 959 "  JOIN pg_type t ON t.typname = c.typname" \
 960 "  JOIN pg_namespace n ON t.typnamespace = n.oid AND n.nspname = c.nspname" \
 961 "  JOIN pg_type a ON a.oid = t.typarray" \
 962 "  ORDER BY idx;"
 963 
 964 /* Lookup types for pre 8.4 servers, including composites.  Arguments are:
 965  *  want_attrs bool, schemas text[], type_names text[]
 966  */
 967 #define LOOKUP_TYPES_PRE_8_4 \
 968 "SELECT idx, t.oid AS typoid, a.oid AS arroid, t.typlen," \
 969 " (" \
 970 "   CASE WHEN %bool THEN (" \
 971 "   SELECT array_to_string" \
 972 "   (" \
 973 "     ARRAY" \
 974 "     (" \
 975 "       SELECT CASE WHEN tt.typtype = 'd' THEN tt.typbasetype " \
 976             "ELSE a.atttypid END ||" \
 977 "         ',' || attlen || ',' || atttypmod || ',' || " \
 978             "encode(attname::bytea, 'hex')" \
 979 "         FROM pg_type b" \
 980 "           JOIN pg_attribute a ON b.typrelid = a.attrelid" \
 981 "           JOIN pg_type tt ON a.atttypid = tt.oid" \
 982 "           WHERE b.oid = t.oid" \
 983 "             AND a.attnum > 0" \
 984 "             AND NOT a.attisdropped" \
 985 "           ORDER BY a.attnum" \
 986 "    ), ' ')" \
 987 "  ) ELSE NULL END) AS arr_props" \
 988 "  FROM" \
 989 "  (" \
 990 "    SELECT n.n AS idx, n.x AS nspname, t.x AS typname" \
 991 "      FROM" \
 992 "      (" \
 993 "        SELECT * FROM information_schema._pg_expandarray(%text[])" \
 994 "      ) n LEFT JOIN" \
 995 "      (" \
 996 "        SELECT * FROM information_schema._pg_expandarray(%text[])" \
 997 "      ) t ON n.n = t.n" \
 998 "        AND n.x IS NOT NULL" \
 999 "        WHERE t.x IS NOT NULL" \
1000 "    UNION ALL" \
1001 "    SELECT n.n AS idx," \
1002 "    (" \
1003 "      SELECT n.nspname from pg_type nt JOIN pg_namespace n ON " \
1004            "nt.typnamespace = n.oid" \
1005 "        JOIN" \
1006 "        (" \
1007 "          SELECT * FROM information_schema._pg_expandarray(" \
1008              "current_schemas(true))" \
1009 "        ) c ON c.x = n.nspname" \
1010 "        WHERE nt.typname = t.x" \
1011 "        ORDER BY c.n LIMIT 1" \
1012 "    ) AS nspname, t.x AS typname" \
1013 "      FROM" \
1014 "      (" \
1015 "        SELECT * FROM information_schema._pg_expandarray($2)" \
1016 "      ) n LEFT JOIN" \
1017 "      (" \
1018 "        SELECT * FROM information_schema._pg_expandarray($3)" \
1019 "      ) t ON n.n = t.n" \
1020 "        AND n.x IS NULL" \
1021 "        WHERE t.x IS NOT NULL" \
1022 "  ) c" \
1023 "  JOIN pg_type t ON t.typname = c.typname" \
1024 "  JOIN pg_namespace n ON t.typnamespace = n.oid AND n.nspname = c.nspname" \
1025 "  JOIN pg_type a ON a.oid = t.typarray" \
1026 "  ORDER BY idx"
1027 
1028 static PGresult *
1029 execLookupTypes(PGconn *conn, PGtypeData *data, PGarray *schemas,
1030   PGarray *names, int want_attrs)
1031 {
1032   if(data->fmtinfo.sversion >= 80400)
1033     return PQexecf(conn, LOOKUP_TYPES, schemas, names, want_attrs);
1034   return PQexecf(conn, LOOKUP_TYPES_PRE_8_4, want_attrs, schemas, names);
1035 }
1036 
1037 static int
1038 sendLookupTypes(PGconn *conn, PGtypeData *data, PGarray *schemas,
1039   PGarray *names, int want_attrs)
1040 {
1041   if(data->fmtinfo.sversion >= 80400)
1042     return PQsendf(conn, LOOKUP_TYPES, schemas, names, want_attrs);
1043   return PQsendf(conn, LOOKUP_TYPES_PRE_8_4, want_attrs, schemas, names);
1044 }
1045 
1046 static int
1047 performRegisterQuery(PGconn *conn, int which, PGregisterType *types,
1048   int count, PGresult **res)
1049 {
1050   int n = FALSE;
1051   PGtypeData *connData;
1052   PGarray names;
1053   PGarray schemas;
1054   int want_attrs = which == PQT_COMPOSITE;
1055 
1056   if (!(connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc)))
1057   {
1058     PQseterror("PGconn is missing event data");
1059     return FALSE;
1060   }
1061 
1062   if (!getTypeParams(conn, types, count, &names, &schemas))
1063     return FALSE;
1064 
1065   if (res)
1066     *res = execLookupTypes(conn, connData, &schemas, &names, want_attrs);
1067   else
1068     n = sendLookupTypes(conn, connData, &schemas, &names, want_attrs);
1069 
1070   PQparamClear(names.param);
1071   PQparamClear(schemas.param);
1072 
1073   if (res)
1074     return *res ? TRUE : FALSE;
1075 
1076   return n;
1077 }