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