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
PQinitTypes(PGconn * conn)147 PQinitTypes(PGconn *conn)
148 {
149 return PQregisterEventProc(conn, pqt_eventproc, "pqtypes", NULL);
150 }
151
152 /* Deprecated */
153 int
PQregisterSubClasses(PGconn * conn,PGregisterType * types,int count)154 PQregisterSubClasses(PGconn *conn, PGregisterType *types, int count)
155 {
156 return PQregisterTypes(conn, PQT_SUBCLASS, types, count, 0);
157 }
158
159 /* Deprecated */
160 int
PQregisterUserDefinedTypes(PGconn * conn,PGregisterType * types,int count)161 PQregisterUserDefinedTypes(PGconn *conn, PGregisterType *types, int count)
162 {
163 return PQregisterTypes(conn, PQT_USERDEFINED, types, count, 0);
164 }
165
166 /* Deprecated */
167 int
PQregisterComposites(PGconn * conn,PGregisterType * types,int count)168 PQregisterComposites(PGconn *conn, PGregisterType *types, int count)
169 {
170 return PQregisterTypes(conn, PQT_COMPOSITE, types, count, 0);
171 }
172
173 int
PQregisterTypes(PGconn * conn,int which,PGregisterType * types,int count,int async)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
PQregisterResult(PGconn * conn,int which,PGregisterType * types,int count,PGresult * res)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
PQclearTypes(PGconn * conn)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 *
pqt_duphandlers(PGtypeHandler * handlers,int hcnt)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
pqt_cleartypes(PGtypeData * typeData)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
pqt_freehandlers(PGtypeHandler * handlers,int hcnt)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 *
pqt_gethandler(PGtypeHandler * handlers,int hcnt,const char * schema,const char * typname)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 *
pqt_gethandlerbyid(PGtypeHandler * handlers,int hcnt,int id)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
pqt_argssuper(PGtypeArgs * args,...)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 *
pqt_fqtn(char * out,size_t outl,const char * schema,const char * typname)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
pqt_allowsptr(PGtypeHandler * h)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
pqt_getfmtinfo(const PGconn * conn,PGtypeFormatInfo * info)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
registerSubClass(PGtypeData * connData,const char * type_name,PGtypeProc typput,PGtypeProc typget)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
expandHandlers(PGtypeData * typeData)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
checkTypeLookups(PGresult * res,PGregisterType * types,int count)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 */
initAttDescs(PGtypeHandler * h,char * attrs)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 */
parseType(const char * spec,char * typschema,char * typname,int typpos)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
getTypeParams(PGconn * conn,PGregisterType * types,int count,PGarray * names,PGarray * schemas)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 *
execLookupTypes(PGconn * conn,PGtypeData * data,PGarray * schemas,PGarray * names,int want_attrs)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
sendLookupTypes(PGconn * conn,PGtypeData * data,PGarray * schemas,PGarray * names,int want_attrs)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
performRegisterQuery(PGconn * conn,int which,PGregisterType * types,int count,PGresult ** res)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 }
1078