libpqtypes - handler.c

Home Page

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