libpqtypes - exec.c

Home Page

array.c
datetime.c
error.c
events.c
exec.c
geo.c
getaddrinfo.h
handler.c
libpqtypes-int.h
libpqtypes.h
misc.c
network.c
numerics.c
param.c
port.c
record.c
regression-test.c
spec.c
utils.c
varlena.c
  1 
  2 /*
  3  * exec.c
  4  *   Query execution and data retrieval functions.
  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  * Each param value requires an oid, ptr and 2 ints (oid, value, length,
 16  * format). This makes the maximum size of a param 20 bytes. A stack size
 17  * of 4k would allow for 204 query params.  If more params are needed,
 18  * heap memory is used. Needing more than 204 param values in a query
 19  * is very rare, although possible.
 20  */
 21 #define PARAM_STACKSIZE 4096
 22 
 23 #define BUILD_ARRAYS(rettype) \
 24   rettype r; \
 25   char *buf   = NULL; \
 26   Oid *oids   = NULL; \
 27   char **vals = NULL; \
 28   int *lens   = NULL; \
 29   int *fmts   = NULL; \
 30   int vcnt    = 0; \
 31   char stackbuffer[PARAM_STACKSIZE]; \
 32 \
 33   PQseterror(NULL); \
 34   if (!conn) \
 35   { \
 36     PQseterror("PGconn cannot be NULL"); \
 37     return (rettype)(0); \
 38   } \
 39 \
 40   if (param) \
 41   { \
 42     buf = stackbuffer; \
 43     if (!buildArrays(param, &buf, &oids, &vals, &lens, &fmts)) \
 44       return (rettype) (0); \
 45     vcnt = param->vcnt; \
 46   }
 47 
 48 #define RETURN_RESULT \
 49   if (param) \
 50   { \
 51     if (buf && buf != stackbuffer) \
 52       free(buf); \
 53   } \
 54   return r
 55 
 56 static int
 57 buildArrays(PGparam *param, char **buf, Oid **oids,
 58   char ***vals, int **lens, int **fmts);
 59 
 60 static PGresult *
 61 copyExecError(PGconn *conn, PGresult *r);
 62 
 63 static const char *
 64 getCommand(PGconn *conn, PGparam *param, const char *command);
 65 
 66 static int
 67 _execvf(PGconn *conn, const char *cmdspec, va_list ap, PGresult **resp);
 68 
 69 int
 70 PQgetf(const PGresult *res, int tup_num, const char *format, ...)
 71 {
 72   int n;
 73   va_list ap;
 74 
 75   va_start(ap, format);
 76   n = PQgetvf(res, tup_num, format, ap);
 77   va_end(ap);
 78 
 79   return n;
 80 }
 81 
 82 int
 83 PQgetvf(const PGresult *res, int tup_num, const char *format, va_list ap)
 84 {
 85   int r;
 86   PGtypeHandler *h;
 87   PGtypeArgs args;
 88   int typpos = 0;
 89   int flags;
 90   Oid ftype;
 91   PGtypeData *resData;
 92   PGtypeSpec *spec = NULL;
 93   char tmp[200];
 94 
 95   PQseterror(NULL);
 96 
 97   if (!res)
 98   {
 99     PQseterror("PGresult cannot be NULL");
100     return FALSE;
101   }
102 
103   if (!(resData = (PGtypeData *) PQresultInstanceData(res, pqt_eventproc)))
104   {
105     PQseterror("PGresult at %p has no event data", res);
106     return FALSE;
107   }
108 
109   va_copy(args.ap, ap);
110 
111   /* "@name" format, lookup typeSpec in cache */
112   if(format && *format == '@')
113   {
114     spec = pqt_getspec(resData->typspecs, resData->typspeccnt, format + 1);
115 
116     /* If we didn't find a type spec, this is an error.  A format string
117      * with a '@' as its first character is reserved.
118      */
119     if (!spec)
120     {
121       PQseterror("No such prepared specifier name: '%s'", format + 1);
122       va_end(args.ap);
123       return FALSE;
124     }
125   }
126 
127   while (format && *format)
128   {
129     if (spec)
130     {
131       /* done, no more handlers in cached spec string. */
132       if (typpos == spec->idcnt)
133         break;
134 
135       h = pqt_gethandlerbyid(resData->typhandlers, resData->typhcnt,
136         spec->idlist[typpos]);
137 
138       /* should be an unusual, or a "will never happen", situation */
139       if (!h)
140       {
141         va_end(args.ap);
142         PQseterror("Unknown type handler id at position %d", typpos+1);
143         return FALSE;
144       }
145 
146       flags = (int) spec->flags[typpos];
147       typpos++;
148     }
149     else
150     {
151       format = pqt_parse(format, resData->typhandlers, resData->typhcnt,
152         NULL, 0, &h, NULL, &typpos, &flags);
153 
154       if (!format)
155       {
156         va_end(args.ap);
157         return FALSE;
158       }
159 
160       if (!h)
161         continue;
162     }
163 
164     if (flags & TYPFLAG_BYNAME)
165       args.get.field_num = PQfnumber(res, va_arg(args.ap, const char *));
166     else
167       args.get.field_num = va_arg(args.ap, int);
168 
169     /* simplify life for handlers by checking getvalue's return here. */
170     if (args.get.field_num < 0 ||
171       !PQgetvalue(res, tup_num, args.get.field_num))
172     {
173       PQseterror(
174         "Invalid tup_num[%d].field_num[%d] (position %d)",
175         tup_num, args.get.field_num, typpos);
176       va_end(args.ap);
177       return FALSE;
178     }
179 
180     ftype = PQftype(res, args.get.field_num);
181     if (((flags & TYPFLAG_ARRAY) && ftype != h->typoid_array) ||
182        (!(flags & TYPFLAG_ARRAY) && ftype != h->typoid))
183     {
184       Oid oid = (flags & TYPFLAG_ARRAY) ? h->typoid_array : h->typoid;
185       PQseterror(
186         "Trying to get type %u '%s' but server returned %u (position %d)",
187         oid, pqt_fqtn(tmp, sizeof(tmp), h->typschema, h->typname),
188         ftype, typpos);
189       va_end(args.ap);
190       return FALSE;
191     }
192 
193     args.is_put       = 0;
194     args.get.result   = (PGresult *) res;
195     args.format       = PQfformat(res, args.get.field_num);
196     args.fmtinfo      = &resData->fmtinfo;
197     args.get.tup_num  = tup_num;
198     args.is_ptr       = (flags & TYPFLAG_POINTER) ? 1 : 0;
199     args.typpos       = typpos;
200     args.typhandler   = h;
201     args.errorf       = pqt_argserrorf;
202     args.super        = pqt_argssuper;
203 
204     if (flags & TYPFLAG_ARRAY)
205       r = pqt_get_array(&args);
206     else
207       r = h->typget(&args);
208 
209     if (r == -1)
210     {
211       va_end(args.ap);
212       return FALSE;
213     }
214   }
215 
216   va_end(args.ap);
217 
218   return TRUE;
219 }
220 
221 /* --------------------------------
222  * Exec and Send functions
223  */
224 
225 PGresult *
226 PQexecf(PGconn *conn, const char *cmdspec, ...)
227 {
228   va_list ap;
229   PGresult *res;
230 
231   va_start(ap, cmdspec);
232   res = PQexecvf(conn, cmdspec, ap);
233   va_end(ap);
234 
235   return res;
236 }
237 
238 PGresult *
239 PQexecvf(PGconn *conn, const char *cmdspec, va_list ap)
240 {
241   PGresult *res;
242   (void) _execvf(conn, cmdspec, ap, &res);
243   return res;
244 }
245 
246 int
247 PQsendf(PGconn *conn, const char *cmdspec, ...)
248 {
249   int n;
250   va_list ap;
251 
252   va_start(ap, cmdspec);
253   n = PQsendvf(conn, cmdspec, ap);
254   va_end(ap);
255 
256   return n;
257 }
258 
259 int
260 PQsendvf(PGconn *conn, const char *cmdspec, va_list ap)
261 {
262   return _execvf(conn, cmdspec, ap, NULL);
263 }
264 
265 PGresult *
266 PQparamExec(PGconn *conn, PGparam *param, const char *command,
267   int resultFormat)
268 {
269   BUILD_ARRAYS(PGresult *);
270 
271   command = getCommand(conn, param, command);
272   if (!command)
273   {
274     r = NULL;
275   }
276   else
277   {
278     r = PQexecParams(conn, command, vcnt, oids,
279       (const char *const * ) vals, lens, fmts, resultFormat);
280 
281     pqt_setresultfields(r);
282     r = copyExecError(conn, r);
283   }
284 
285   RETURN_RESULT;
286 }
287 
288 int
289 PQparamSendQuery(PGconn *conn, PGparam *param, const char *command,
290   int resultFormat)
291 {
292   BUILD_ARRAYS(int);
293 
294   command = getCommand(conn, param, command);
295   if (!command)
296   {
297     r = FALSE;
298   }
299   else
300   {
301     r = PQsendQueryParams(conn, command, vcnt, oids,
302       (const char *const * ) vals, lens, fmts, resultFormat);
303 
304     if (!r)
305       PQseterror("PGconn: %s", PQerrorMessage(conn));
306   }
307 
308   RETURN_RESULT;
309 }
310 
311 PGresult *
312 PQparamExecPrepared(PGconn *conn, PGparam *param, const char *stmtName,
313   int resultFormat)
314 {
315   BUILD_ARRAYS(PGresult *);
316 
317   r = PQexecPrepared(conn, stmtName, vcnt, (const char *const * ) vals,
318     lens, fmts, resultFormat);
319 
320   pqt_setresultfields(r);
321   r = copyExecError(conn, r);
322 
323   RETURN_RESULT;
324 }
325 
326 int
327 PQparamSendQueryPrepared(PGconn *conn, PGparam *param, const char *stmtName,
328   int resultFormat)
329 {
330   BUILD_ARRAYS(int);
331 
332   r = PQsendQueryPrepared(conn, stmtName, vcnt,
333     (const char *const * ) vals, lens, fmts, resultFormat);
334 
335   if (!r)
336     PQseterror("PGconn: %s", PQerrorMessage(conn));
337 
338   RETURN_RESULT;
339 }
340 
341 /* Called by PQexecvf and PQsendvf.  When resp is NULL, PQparamSendQuery
342  * is used to execute the command.  Otherwise, PQparamExec is used.  The
343  * return value is always zero when resp is not NULL.  When resp is NULL,
344  * the return value is zero for error and non-zero for success (identical
345  * to PQparamSendQuery).
346  */
347 static int
348 _execvf(PGconn *conn, const char *cmdspec, va_list ap, PGresult **resp)
349 {
350   int retval = 0;
351   size_t stmt_len=0;
352   char buffer[8192]; /* could be larger these days but be conservative */
353   char *stmt = NULL;
354   PGparam *param = NULL;
355 
356   if (resp)
357     *resp = NULL;
358 
359   if(!conn)
360   {
361     PQseterror("PGconn cannot be NULL");
362     return FALSE;
363   }
364 
365   if(!cmdspec || !*cmdspec)
366   {
367     PQseterror("cmdspec cannot be NULL or an empty string");
368     return FALSE;
369   }
370 
371   /* No stmt buf required for preapred type specs */
372   if (*cmdspec != '@')
373   {
374     /* The resulting parameterized command is guarenteed to be smaller
375      * than the cmdspec.  When needed, enlarge stmt buf to cmdspec length.
376      */
377     stmt_len = strlen(cmdspec) + 1;
378 
379     /* stack buffer is too small, use heap */
380     if (stmt_len > sizeof(buffer))
381     {
382       if (!(stmt = (char *) malloc(stmt_len)))
383       {
384         PQseterror(PQT_OUTOFMEMORY);
385         return FALSE;
386       }
387     }
388     else
389     {
390       stmt = buffer;
391       stmt_len = sizeof(buffer);
392     }
393   }
394 
395   if ((param = PQparamCreate(conn)))
396   {
397     if (PQputvf(param, stmt, stmt_len, cmdspec, ap))
398     {
399       const char *s = stmt ? stmt : cmdspec;
400 
401       if (resp)
402         *resp = PQparamExec(conn, param, s, 1);
403       else
404         retval = PQparamSendQuery(conn, param, s, 1);
405     }
406 
407     PQparamClear(param);
408   }
409 
410   if (stmt && stmt != buffer)
411     free(stmt);
412 
413   return retval;
414 }
415 
416 /*
417  * Assigns param values to param arrays, for use with postgres
418  * parameter API. 'buf' is expected to be PARAM_STACKSIZE bytes.  If more
419  * memory is required, memory is allocated and assigned to *buf, which
420  * must be freed by caller.  To determine if *buf was allocated, compare
421  * its address to the initially provided stack address.
422  * Returns 1 on success and 0 on error.
423  */
424 static int
425 buildArrays(PGparam *param, char **buf, Oid **oids,
426   char ***vals, int **lens, int **fmts)
427 {
428   int n;
429 
430   /* no params to assign */
431   if (param->vcnt == 0)
432     return 1;
433 
434   /* required memory size for the 4 param arrays */
435   n = (int) ((sizeof(void *) * param->vcnt) + /* values */
436       ((sizeof(int) * 2) * param->vcnt) +     /* lengths and formats */
437       (sizeof(Oid) * param->vcnt));           /* oids */
438 
439   /* required memory is too large for stack buffer, get some heap */
440   if (n > PARAM_STACKSIZE)
441   {
442     char *p;
443 
444     if (!(p = (char *) malloc(n)))
445     {
446       PQseterror(PQT_OUTOFMEMORY);
447       return 0;
448     }
449 
450     *buf = p;
451   }
452 
453   /* give arrays memory from buffer, which could be stack or heap. */
454   *vals = (char **) *buf;
455   *lens = (int *) (*buf + (sizeof(void *) * param->vcnt));
456   *fmts = (*lens) + param->vcnt;
457   *oids = (Oid *) ((*fmts) + param->vcnt);
458 
459   /* loop param values and assign value, length, format
460    * and oid to arrays.
461    */
462   for (n=0; n < param->vcnt; n++)
463   {
464     (*oids)[n] = param->vals[n].oid;
465     (*vals)[n] = param->vals[n].data;
466     (*lens)[n] = param->vals[n].datal;
467     (*fmts)[n] = param->vals[n].format;
468   }
469 
470   return 1;
471 }
472 
473 static PGresult *
474 copyExecError(PGconn *conn, PGresult *r)
475 {
476   if (!r)
477   {
478     PQseterror("PGconn: %s", PQerrorMessage(conn));
479     return NULL;
480   }
481 
482   switch (PQresultStatus(r))
483   {
484     case PGRES_COMMAND_OK:
485     case PGRES_TUPLES_OK:
486     case PGRES_EMPTY_QUERY:
487       break;
488 
489     default:
490     {
491       PQseterror("PGresult: %s", PQresultErrorMessage(r));
492       PQclear(r);
493       r = NULL;
494       break;
495     }
496   }
497 
498   return r;
499 }
500 
501 /* Using the param is preferred when both conn and param are provided.
502  * The conn is there in case the exec has no parameters, NULL param.
503  */
504 static const char *
505 getCommand(PGconn *conn, PGparam *param, const char *command)
506 {
507   PGtypeSpec *spec;
508   int typspeccnt = 0;
509   PGtypeSpec *typspecs = NULL;
510 
511   if (!command)
512   {
513     PQseterror("command to execute cannot be NULL");
514     return NULL;
515   }
516 
517   if (*command != '@')
518     return command;
519 
520   if (param)
521   {
522     typspecs = param->typspecs;
523     typspeccnt = param->typspeccnt;
524   }
525 
526   /* Try to get instance data from the conn */
527   if (!typspecs || typspeccnt == 0)
528   {
529     PGtypeData *data = PQinstanceData(conn, pqt_eventproc);
530 
531     if (!data)
532     {
533       PQseterror("PGconn at %p has no event data", conn);
534       return NULL;
535     }
536 
537     typspecs = data->typspecs;
538     typspeccnt = data->typspeccnt;
539   }
540 
541   spec = pqt_getspec(typspecs, typspeccnt, command + 1);
542 
543   /* If we didn't find a type spec, this is an error.  A format string
544    * with an '@' as its first character is reserved.
545    */
546   if (!spec)
547   {
548     PQseterror("No such prepared specifier name: '%s'", command + 1);
549     return NULL;
550   }
551 
552   /* make sure type spec was prepared with a statement */
553   if (!spec->stmt || !*spec->stmt)
554   {
555     PQseterror("Prepared specifier name '%s' has no statement", command + 1);
556     return NULL;
557   }
558 
559   return (const char *) spec->stmt;
560 }