libpqtypes - param.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  * param.c
  4  *   Management functions for the PGparam object.
  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 static int argsExpandBuffer(PGtypeArgs *args, int new_len);
 15 
 16 PGparam *
 17 PQparamCreate(const PGconn *conn)
 18 {
 19   PGparam *param;
 20   PGtypeData *connData;
 21 
 22   PQseterror(NULL);
 23 
 24   if (!conn)
 25   {
 26     PQseterror("PGconn cannot be NULL");
 27     return NULL;
 28   }
 29 
 30   param = (PGparam *) malloc(sizeof(PGparam));
 31   if (!param)
 32   {
 33     PQseterror(PQT_OUTOFMEMORY);
 34     return NULL;
 35   }
 36 
 37   memset(param, 0, sizeof(PGparam));
 38 
 39   connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc);
 40   if (!connData)
 41   {
 42     PQparamClear(param);
 43     PQseterror("No type data exists for PGconn at %p", conn);
 44     return NULL;
 45   }
 46 
 47   /* copy any handlers from conn object */
 48   if (connData->typhcnt > 0)
 49   {
 50     param->typhandlers = pqt_duphandlers(
 51       connData->typhandlers, connData->typhcnt);
 52 
 53     if (!param->typhandlers)
 54     {
 55       PQparamClear(param);
 56       PQseterror(PQT_OUTOFMEMORY);
 57       return NULL;
 58     }
 59 
 60     param->typhcnt = connData->typhcnt;
 61   }
 62 
 63   /* copy any typespecs from conn object */
 64   if (connData->typspeccnt > 0)
 65   {
 66     param->typspecs = pqt_dupspecs(
 67       connData->typspecs, connData->typspeccnt);
 68 
 69     if (!param->typspecs)
 70     {
 71       PQparamClear(param);
 72       PQseterror(PQT_OUTOFMEMORY);
 73       return NULL;
 74     }
 75 
 76     param->typspeccnt = connData->typspeccnt;
 77   }
 78 
 79   pqt_getfmtinfo(conn, &param->fmtinfo);
 80   return param;
 81 }
 82 
 83 PQT_EXPORT PGparam *
 84 PQparamDup(PGparam *param)
 85 {
 86   PGparam *new_param;
 87 
 88   PQseterror(NULL);
 89 
 90   if (!param)
 91   {
 92     PQseterror("PGparam to duplicate cannot be NULL");
 93     return NULL;
 94   }
 95 
 96   new_param = (PGparam *) malloc(sizeof(PGparam));
 97   if (!new_param)
 98   {
 99     PQseterror(PQT_OUTOFMEMORY);
100     return NULL;
101   }
102 
103   memset(new_param, 0, sizeof(PGparam));
104 
105   /* copy any handlers from conn object */
106   if (param->typhcnt > 0)
107   {
108     new_param->typhandlers = pqt_duphandlers(
109       param->typhandlers, param->typhcnt);
110 
111     if (!new_param->typhandlers)
112     {
113       PQparamClear(new_param);
114       PQseterror(PQT_OUTOFMEMORY);
115       return NULL;
116     }
117 
118     new_param->typhcnt = param->typhcnt;
119   }
120 
121   /* copy any typespecs from conn object */
122   if (param->typspeccnt > 0)
123   {
124     new_param->typspecs = pqt_dupspecs(
125       param->typspecs, param->typspeccnt);
126 
127     if (!new_param->typspecs)
128     {
129       PQparamClear(new_param);
130       PQseterror(PQT_OUTOFMEMORY);
131       return NULL;
132     }
133 
134     new_param->typspeccnt = param->typspeccnt;
135   }
136 
137   memcpy(&new_param->fmtinfo, &param->fmtinfo, sizeof(PGtypeFormatInfo));
138 
139   /* copy any values, don't bother if array is empty. */
140   if (param->vcnt > 0)
141   {
142     int i;
143 
144     for (i=0; i < param->vcnt; i++)
145     {
146       int flags = 0;
147       PGvalue *val = &param->vals[i];
148 
149       /* Is val->data is direct user pointer?  If so, set pointer flag
150        * so pqt_putparam doesn't deep copy it.
151        */
152       if (val->ptr != val->data)
153         flags |= TYPFLAG_POINTER;
154 
155       if (!pqt_putparam(new_param, val->data, val->datal, flags,
156         val->format, val->oid))
157       {
158         PQparamClear(new_param);
159         return NULL;
160       }
161     }
162   }
163 
164   return new_param;
165 }
166 
167 int
168 PQparamCount(PGparam *param)
169 {
170   if (param)
171     return param->vcnt;
172   return 0;
173 }
174 
175 void
176 PQparamReset(PGparam *param)
177 {
178   if (param)
179     param->vcnt = 0;
180 }
181 
182 void
183 PQparamClear(PGparam *param)
184 {
185   int i;
186 
187   if (!param)
188     return;
189 
190   /* vmax is the size of the vals array.  We don't use vcnt because
191    * someone could of just called PQparamReset, leaving vcnt 0.  If
192    * we find any value pointers that are not NULL, free'm.
193    */
194   for (i=0; i < param->vmax; i++)
195     if (param->vals[i].ptr)
196       free(param->vals[i].ptr);
197 
198   if (param->vals)
199     free(param->vals);
200 
201   pqt_freehandlers(param->typhandlers, param->typhcnt);
202   pqt_freespecs(param->typspecs, param->typspeccnt);
203 
204   param->vmax = 0;
205   param->vcnt = 0;
206   param->vals = NULL;
207   param->typhcnt = 0;
208   param->typhandlers = NULL;
209   param->typspeccnt = 0;
210   param->typspecs = NULL;
211 
212   free(param);
213 }
214 
215 int
216 PQputf(PGparam *param, const char *format, ...)
217 {
218   int r;
219   va_list ap;
220 
221   /* This function is just a wrapper to PQputvf */
222   va_start(ap, format);
223   r = PQputvf(param, NULL, 0, format, ap);
224   va_end(ap);
225 
226   return r;
227 }
228 
229 int
230 PQputvf(PGparam *param, char *stmtBuf, size_t stmtBufLen,
231   const char *format, va_list ap)
232 {
233   int n=0;
234   int flags;
235   size_t stmtPos = 0;
236   int save_vcnt;
237   int typpos = 0;
238   PGtypeHandler *h;
239   PGtypeArgs args;
240   char args_outbuf[4096];
241   PGtypeSpec *spec = NULL;
242 
243   PQseterror(NULL);
244 
245   if (!param)
246   {
247     PQseterror("PGparam cannot be NULL");
248     return FALSE;
249   }
250 
251   if (!format || !*format)
252   {
253     PQseterror("param 'format' cannot be NULL or an empty string");
254     return FALSE;
255   }
256 
257   if (stmtBuf && stmtBufLen < 1)
258   {
259     PQseterror("Invalid argument: stmtBufLen must be >= 1");
260     return FALSE;
261   }
262 
263   save_vcnt = param->vcnt;
264   va_copy(args.ap, ap);
265 
266   /* "@name" format, lookup typeSpec in cache */
267   if(format && *format == '@')
268   {
269 
270     spec = pqt_getspec(param->typspecs, param->typspeccnt, format + 1);
271 
272     /* If we didn't find a type spec, this is an error.  A format string
273      * with a '@' as its first character is reserved.
274      */
275     if (!spec)
276     {
277       PQseterror("No such prepared specifier name: '%s'", format + 1);
278       return FALSE;
279     }
280   }
281 
282   while (format && *format)
283   {
284     if (spec)
285     {
286       /* done, no more handlers in cached spec string. */
287       if (typpos == spec->idcnt)
288         break;
289 
290       h = pqt_gethandlerbyid(param->typhandlers, param->typhcnt,
291         spec->idlist[typpos]);
292 
293       /* should be an unusual, or a "will never happen", situation */
294       if (!h)
295       {
296         va_end(args.ap);
297         PQseterror("Unknown type handler id at position %d", typpos+1);
298         param->vcnt = save_vcnt;
299         return FALSE;
300       }
301 
302       flags = (int) spec->flags[typpos];
303       typpos++;
304     }
305     else
306     {
307       format = pqt_parse(format, param->typhandlers, param->typhcnt,
308         stmtBuf, stmtBufLen, &h, &stmtPos, &typpos, &flags);
309 
310       if (!format)
311       {
312         param->vcnt = save_vcnt;
313         return FALSE;
314       }
315 
316       if (!h)
317         continue;
318     }
319 
320     args.is_put           = 1;
321     args.put.param        = param;
322     args.fmtinfo          = &param->fmtinfo;
323     args.put.out          = args_outbuf;
324     args.put.__allocated_out = NULL;
325     args.put.outl         = (int) sizeof(args_outbuf);
326     args.is_ptr           = (flags & TYPFLAG_POINTER) ? 1 : 0;
327     args.format           = BINARYFMT;
328     args.put.expandBuffer = argsExpandBuffer;
329     args.typpos           = typpos;
330     args.typhandler       = h;
331     args.errorf           = pqt_argserrorf;
332     args.super            = pqt_argssuper;
333     *args.put.out         = 0;
334 
335     if (flags & TYPFLAG_ARRAY)
336       n = pqt_put_array(&args);
337     else
338       n = h->typput(&args);
339 
340     if (n == -1)
341     {
342       if (args.put.__allocated_out && args.put.__allocated_out != args_outbuf)
343         free(args.put.__allocated_out);
344       param->vcnt = save_vcnt;
345       return FALSE;
346     }
347 
348     if (args.put.out == NULL)
349     {
350       args.format = BINARYFMT;
351       n = -1;
352     }
353 
354     n = pqt_putparam(param, args.put.out, n, flags, args.format,
355       (flags & TYPFLAG_ARRAY) ? h->typoid_array : h->typoid);
356 
357     if (args.put.__allocated_out && args.put.__allocated_out != args_outbuf)
358       free(args.put.__allocated_out);
359 
360     if (!n)
361     {
362       param->vcnt = save_vcnt;
363       return FALSE;
364     }
365   }
366 
367   if (stmtBuf)
368     stmtBuf[stmtPos] = 0;
369 
370   return TRUE;
371 }
372 
373 
374 /* ----------------------------
375  * Helper functions
376  */
377 
378 int
379 pqt_putparam(PGparam *param, const void *data, int datal,
380   int flags, int format, Oid typoid)
381 {
382   PGvalue *v;
383 
384   if (!param)
385     return FALSE;
386 
387   if (!data)
388     datal = -1;
389 
390   /* need to grow param vals array */
391   if (param->vcnt == param->vmax)
392   {
393     PGvalue *vals;
394     int vmax = param->vmax ? (param->vmax * 3) / 2 : 16;
395 
396     vals = (PGvalue *) pqt_realloc(param->vals, sizeof(PGvalue) * vmax);
397     if (!vals)
398     {
399       PQseterror(PQT_OUTOFMEMORY);
400       return FALSE;
401     }
402 
403     /* zero out the new array elements */
404     memset(vals + param->vcnt, 0, (vmax - param->vcnt) * sizeof(PGvalue));
405     param->vmax = vmax;
406     param->vals = vals;
407   }
408 
409   /* grab next param value */
410   v = &param->vals[param->vcnt];
411 
412   if (datal == -1)
413   {
414     v->data = NULL;
415   }
416   /* wants to put a direct pointer */
417   else if (flags & TYPFLAG_POINTER)
418   {
419     v->data = (char *) data;
420   }
421   else
422   {
423     /* need more mem for param value ptr */
424     if (v->ptrl < datal)
425     {
426       char *ptr = (char *) pqt_realloc(v->ptr, datal);
427 
428       if (!ptr)
429       {
430         PQseterror(PQT_OUTOFMEMORY);
431         return FALSE;
432       }
433 
434       v->ptrl = datal;
435       v->ptr = ptr;
436     }
437 
438     memcpy(v->ptr, data, datal);
439     v->data = v->ptr;
440   }
441 
442   v->datal  = datal;
443   v->format = format;
444   v->oid    = typoid;
445   param->vcnt++;
446   return TRUE;
447 }
448 
449 /* returns 0 on success and -1 on error */
450 static int
451 argsExpandBuffer(PGtypeArgs *args, int new_len)
452 {
453   char *new_out;
454 
455   if (new_len <= args->put.outl)
456     return 0;
457 
458   if (!args->put.__allocated_out)
459   {
460     new_out = (char *) malloc(new_len);
461     if (!new_out)
462       return args->errorf(args, PQT_OUTOFMEMORY);
463 
464     /* fully copy existing stack buffer, we don't know what data
465      * the user has already written and may want to keep.
466      */
467     memcpy(new_out, args->put.out, args->put.outl);
468   }
469   else
470   {
471     new_out = (char *) realloc(args->put.__allocated_out, new_len);
472     if (!new_out)
473       return args->errorf(args, PQT_OUTOFMEMORY);
474   }
475 
476   args->put.out = args->put.__allocated_out = new_out;
477   args->put.outl = new_len;
478   return 0;
479 }