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, ®type, 1); 145 146 /* Composite registration, no inheritence operator and no typput/typget */ 147 if (!typput && !typget) 148 return PQregisterComposites(conn, ®type, 1); 149 150 return PQregisterUserDefinedTypes(conn, ®type, 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 } |