libpqtypes - exec.c

Home Page

  1 
  2 /*
  3  * exec.c
  4  *   Query execution and data retrieval functions.
  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  * Each param value requires an oid, ptr and 2 ints (oid, value, length, format).
 16  * This makes the maximum size of a param 20 bytes. A stack size of 4k
 17  * 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 || !PQgetvalue(res, tup_num, args.get.field_num))
171     {
172       PQseterror(
173         "Invalid tup_num[%d].field_num[%d] (position %d)",
174         tup_num, args.get.field_num, typpos);
175       va_end(args.ap);
176       return FALSE;
177     }
178 
179     ftype = PQftype(res, args.get.field_num);
180     if (((flags & TYPFLAG_ARRAY) && ftype != h->typoid_array) ||
181        (!(flags & TYPFLAG_ARRAY) && ftype != h->typoid))
182     {
183       Oid oid = (flags & TYPFLAG_ARRAY) ? h->typoid_array : h->typoid;
184       PQseterror(
185         "Trying to get type %u '%s' but server returned %u (position %d)",
186         oid, pqt_fqtn(tmp, sizeof(tmp), h->typschema, h->typname),
187         ftype, typpos);
188       va_end(args.ap);
189       return FALSE;
190     }
191 
192     args.is_put       = 0;
193     args.get.result   = (PGresult *) res;
194     args.format       = PQfformat(res, args.get.field_num);
195     args.fmtinfo      = &resData->fmtinfo;
196     args.get.tup_num  = tup_num;
197     args.is_ptr       = (flags & TYPFLAG_POINTER) ? 1 : 0;
198     args.typpos       = typpos;
199     args.typhandler   = h;
200     args.errorf       = pqt_argserrorf;
201     args.super        = pqt_argssuper;
202 
203     if (flags & TYPFLAG_ARRAY)
204       r = pqt_get_array(&args);
205     else
206       r = h->typget(&args);
207 
208     if (r == -1)
209     {
210       va_end(args.ap);
211       return FALSE;
212     }
213   }
214 
215   va_end(args.ap);
216 
217   return TRUE;
218 }
219 
220 /* --------------------------------
221  * Exec and Send functions
222  */
223 
224 PGresult *
225 PQexecf(PGconn *conn, const char *cmdspec, ...)
226 {
227   va_list ap;
228   PGresult *res;
229 
230   va_start(ap, cmdspec);
231   res = PQexecvf(conn, cmdspec, ap);
232   va_end(ap);
233 
234   return res;
235 }
236 
237 PGresult *
238 PQexecvf(PGconn *conn, const char *cmdspec, va_list ap)
239 {
240   PGresult *res;
241   (void) _execvf(conn, cmdspec, ap, &res);
242   return res;
243 }
244 
245 int
246 PQsendf(PGconn *conn, const char *cmdspec, ...)
247 {
248   int n;
249   va_list ap;
250 
251   va_start(ap, cmdspec);
252   n = PQsendvf(conn, cmdspec, ap);
253   va_end(ap);
254 
255   return n;
256 }
257 
258 int
259 PQsendvf(PGconn *conn, const char *cmdspec, va_list ap)
260 {
261   return _execvf(conn, cmdspec, ap, NULL);
262 }
263 
264 PGresult *
265 PQparamExec(PGconn *conn, PGparam *param, const char *command,
266   int resultFormat)
267 {
268   BUILD_ARRAYS(PGresult *);
269 
270   command = getCommand(conn, param, command);
271   if (!command)
272   {
273     r = NULL;
274   }
275   else
276   {
277     r = PQexecParams(conn, command, vcnt, oids,
278       (const char *const * ) vals, lens, fmts, resultFormat);
279 
280     pqt_setresultfields(r);
281     r = copyExecError(conn, r);
282   }
283 
284   RETURN_RESULT;
285 }
286 
287 int
288 PQparamSendQuery(PGconn *conn, PGparam *param, const char *command,
289   int resultFormat)
290 {
291   BUILD_ARRAYS(int);
292 
293   command = getCommand(conn, param, command);
294   if (!command)
295   {
296     r = FALSE;
297   }
298   else
299   {
300     r = PQsendQueryParams(conn, command, vcnt, oids,
301       (const char *const * ) vals, lens, fmts, resultFormat);
302 
303     if (!r)
304       PQseterror("PGconn: %s", PQerrorMessage(conn));
305   }
306 
307   RETURN_RESULT;
308 }
309 
310 PGresult *
311 PQparamExecPrepared(PGconn *conn, PGparam *param, const char *stmtName,
312   int resultFormat)
313 {
314   BUILD_ARRAYS(PGresult *);
315 
316   r = PQexecPrepared(conn, stmtName, vcnt, (const char *const * ) vals,
317     lens, fmts, resultFormat);
318 
319   pqt_setresultfields(r);
320   r = copyExecError(conn, r);
321 
322   RETURN_RESULT;
323 }
324 
325 int
326 PQparamSendQueryPrepared(PGconn *conn, PGparam *param, const char *stmtName,
327   int resultFormat)
328 {
329   BUILD_ARRAYS(int);
330 
331   r = PQsendQueryPrepared(conn, stmtName, vcnt,
332     (const char *const * ) vals, lens, fmts, resultFormat);
333 
334   if (!r)
335     PQseterror("PGconn: %s", PQerrorMessage(conn));
336 
337   RETURN_RESULT;
338 }
339 
340 /* Called by PQexecvf and PQsendvf.  When resp is NULL, PQparamSendQuery
341  * is used to execute the command.  Otherwise, PQparamExec is used.  The
342  * return value is always zero when resp is not NULL.  When resp is NULL,
343  * the return value is zero for error and non-zero for success (identical
344  * to PQparamSendQuery).
345  */
346 static int
347 _execvf(PGconn *conn, const char *cmdspec, va_list ap, PGresult **resp)
348 {
349   int retval = 0;
350   size_t stmt_len=0;
351   char buffer[8192]; /* could be larger these days but be conservative */
352   char *stmt = NULL;
353   PGparam *param = NULL;
354 
355   if (resp)
356     *resp = NULL;
357 
358   if(!conn)
359   {
360     PQseterror("PGconn cannot be NULL");
361     return FALSE;
362   }
363 
364   if(!cmdspec || !*cmdspec)
365   {
366     PQseterror("cmdspec cannot be NULL or an empty string");
367     return FALSE;
368   }
369 
370   /* No stmt buf required for preapred type specs */
371   if (*cmdspec != '@')
372   {
373     /* The resulting parameterized command is guarenteed to be smaller
374      * than the cmdspec.  When needed, enlarge stmt buf to cmdspec length.
375      */
376     stmt_len = strlen(cmdspec) + 1;
377 
378     /* stack buffer is too small, use heap */
379     if (stmt_len > sizeof(buffer))
380     {
381       if (!(stmt = (char *) malloc(stmt_len)))
382       {
383         PQseterror(PQT_OUTOFMEMORY);
384         return FALSE;
385       }
386     }
387     else
388     {
389       stmt = buffer;
390       stmt_len = sizeof(buffer);
391     }
392   }
393 
394   if ((param = PQparamCreate(conn)))
395   {
396     if (PQputvf(param, stmt, stmt_len, cmdspec, ap))
397     {
398       const char *s = stmt ? stmt : cmdspec;
399 
400       if (resp)
401         *resp = PQparamExec(conn, param, s, 1);
402       else
403         retval = PQparamSendQuery(conn, param, s, 1);
404     }
405 
406     PQparamClear(param);
407   }
408 
409   if (stmt && stmt != buffer)
410     free(stmt);
411 
412   return retval;
413 }
414 
415 /*
416  * Assigns param values to param arrays, for use with postgres
417  * parameter API. 'buf' is expected to be PARAM_STACKSIZE bytes.  If more
418  * memory is required, memory is allocated and assigned to *buf, which
419  * must be freed by caller.  To determine if *buf was allocated, compare
420  * its address to the initially provided stack address.
421  * Returns 1 on success and 0 on error.
422  */
423 static int
424 buildArrays(PGparam *param, char **buf, Oid **oids,
425   char ***vals, int **lens, int **fmts)
426 {
427   int n;
428 
429   /* no params to assign */
430   if (param->vcnt == 0)
431     return 1;
432 
433   /* required memory size for the 4 param arrays */
434   n = (int) ((sizeof(void *) * param->vcnt) + /* values */
435       ((sizeof(int) * 2) * param->vcnt) +     /* lengths and formats */
436       (sizeof(Oid) * param->vcnt));           /* oids */
437 
438   /* required memory is too large for stack buffer, get some heap */
439   if (n > PARAM_STACKSIZE)
440   {
441     char *p;
442 
443     if (!(p = (char *) malloc(n)))
444     {
445       PQseterror(PQT_OUTOFMEMORY);
446       return 0;
447     }
448 
449     *buf = p;
450   }
451 
452   /* give arrays memory from buffer, which could be stack or heap. */
453   *vals = (char **) *buf;
454   *lens = (int *) (*buf + (sizeof(void *) * param->vcnt));
455   *fmts = (*lens) + param->vcnt;
456   *oids = (Oid *) ((*fmts) + param->vcnt);
457 
458   /* loop param values and assign value, length, format and oid to arrays. */
459   for (n=0; n < param->vcnt; n++)
460   {
461     (*oids)[n] = param->vals[n].oid;
462     (*vals)[n] = param->vals[n].data;
463     (*lens)[n] = param->vals[n].datal;
464     (*fmts)[n] = param->vals[n].format;
465   }
466 
467   return 1;
468 }
469 
470 static PGresult *
471 copyExecError(PGconn *conn, PGresult *r)
472 {
473   if (!r)
474   {
475     PQseterror("PGconn: %s", PQerrorMessage(conn));
476     return NULL;
477   }
478 
479   switch (PQresultStatus(r))
480   {
481     case PGRES_COMMAND_OK:
482     case PGRES_TUPLES_OK:
483     case PGRES_EMPTY_QUERY:
484       break;
485 
486     default:
487     {
488       PQseterror("PGresult: %s", PQresultErrorMessage(r));
489       PQclear(r);
490       r = NULL;
491       break;
492     }
493   }
494 
495   return r;
496 }
497 
498 /* Using the param is preferred when both conn and param are provided.
499  * The conn is there in case the exec has no parameters, NULL param.
500  */
501 static const char *
502 getCommand(PGconn *conn, PGparam *param, const char *command)
503 {
504   PGtypeSpec *spec;
505   int typspeccnt = 0;
506   PGtypeSpec *typspecs = NULL;
507 
508   if (!command)
509   {
510     PQseterror("command to execute cannot be NULL");
511     return NULL;
512   }
513 
514   if (*command != '@')
515     return command;
516 
517   if (param)
518   {
519     typspecs = param->typspecs;
520     typspeccnt = param->typspeccnt;
521   }
522 
523   /* Try to get instance data from the conn */
524   if (!typspecs || typspeccnt == 0)
525   {
526     PGtypeData *data = PQinstanceData(conn, pqt_eventproc);
527 
528     if (!data)
529     {
530       PQseterror("PGconn at %p has no event data", conn);
531       return NULL;
532     }
533 
534     typspecs = data->typspecs;
535     typspeccnt = data->typspeccnt;
536   }
537 
538   spec = pqt_getspec(typspecs, typspeccnt, command + 1);
539 
540   /* If we didn't find a type spec, this is an error.  A format string
541    * with an '@' as its first character is reserved.
542    */
543   if (!spec)
544   {
545     PQseterror("No such prepared specifier name: '%s'", command + 1);
546     return NULL;
547   }
548 
549   /* make sure type spec was prepared with a statement */
550   if (!spec->stmt || !*spec->stmt)
551   {
552     PQseterror("Prepared specifier name '%s' has no statement", command + 1);
553     return NULL;
554   }
555 
556   return (const char *) spec->stmt;
557 }