libpqtypes - param.c

Home Page

  1 
  2 /*
  3  * param.c
  4  *   Management functions for the PGparam object.
  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 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 int
 84 PQparamCount(PGparam *param)
 85 {
 86   if (param)
 87     return param->vcnt;
 88   return 0;
 89 }
 90 
 91 void
 92 PQparamReset(PGparam *param)
 93 {
 94   if (param)
 95     param->vcnt = 0;
 96 }
 97 
 98 void
 99 PQparamClear(PGparam *param)
100 {
101   int i;
102 
103   if (!param)
104     return;
105 
106   /* vmax is the size of the vals array.  We don't use vcnt because
107    * someone could of just called PQparamReset, leaving vcnt 0.  If
108    * we find any value pointers that are not NULL, free'm.
109    */
110   for (i=0; i < param->vmax; i++)
111     if (param->vals[i].ptr)
112       free(param->vals[i].ptr);
113 
114   if (param->vals)
115     free(param->vals);
116 
117   pqt_freehandlers(param->typhandlers, param->typhcnt);
118   pqt_freespecs(param->typspecs, param->typspeccnt);
119 
120   param->vmax = 0;
121   param->vcnt = 0;
122   param->vals = NULL;
123   param->typhcnt = 0;
124   param->typhandlers = NULL;
125   param->typspeccnt = 0;
126   param->typspecs = NULL;
127 
128   free(param);
129 }
130 
131 int
132 PQputf(PGparam *param, const char *format, ...)
133 {
134   int r;
135   va_list ap;
136 
137   /* This function is just a wrapper to PQputvf */
138   va_start(ap, format);
139   r = PQputvf(param, NULL, 0, format, ap);
140   va_end(ap);
141 
142   return r;
143 }
144 
145 int
146 PQputvf(PGparam *param, char *stmtBuf, size_t stmtBufLen,
147   const char *format, va_list ap)
148 {
149   int n=0;
150   int flags;
151   size_t stmtPos = 0;
152   int save_vcnt;
153   int typpos = 0;
154   PGtypeHandler *h;
155   PGtypeArgs args;
156   char args_outbuf[4096];
157   PGtypeSpec *spec = NULL;
158 
159   PQseterror(NULL);
160 
161   if (!param)
162   {
163     PQseterror("PGparam cannot be NULL");
164     return FALSE;
165   }
166 
167   if (!format || !*format)
168   {
169     PQseterror("param 'format' cannot be NULL or an empty string");
170     return FALSE;
171   }
172 
173   if (stmtBuf && stmtBufLen < 1)
174   {
175     PQseterror("Invalid argument: stmtBufLen must be >= 1");
176     return FALSE;
177   }
178 
179   save_vcnt = param->vcnt;
180   va_copy(args.ap, ap);
181 
182   /* "@name" format, lookup typeSpec in cache */
183   if(format && *format == '@')
184   {
185 
186     spec = pqt_getspec(param->typspecs, param->typspeccnt, format + 1);
187 
188     /* If we didn't find a type spec, this is an error.  A format string
189      * with a '@' as its first character is reserved.
190      */
191     if (!spec)
192     {
193       PQseterror("No such prepared specifier name: '%s'", format + 1);
194       return FALSE;
195     }
196   }
197 
198   while (format && *format)
199   {
200     if (spec)
201     {
202       /* done, no more handlers in cached spec string. */
203       if (typpos == spec->idcnt)
204         break;
205 
206       h = pqt_gethandlerbyid(param->typhandlers, param->typhcnt,
207         spec->idlist[typpos]);
208 
209       /* should be an unusual, or a "will never happen", situation */
210       if (!h)
211       {
212         va_end(args.ap);
213         PQseterror("Unknown type handler id at position %d", typpos+1);
214         param->vcnt = save_vcnt;
215         return FALSE;
216       }
217 
218       flags = (int) spec->flags[typpos];
219       typpos++;
220     }
221     else
222     {
223       format = pqt_parse(format, param->typhandlers, param->typhcnt,
224         stmtBuf, stmtBufLen, &h, &stmtPos, &typpos, &flags);
225 
226       if (!format)
227       {
228         param->vcnt = save_vcnt;
229         return FALSE;
230       }
231 
232       if (!h)
233         continue;
234     }
235 
236     args.is_put           = 1;
237     args.put.param        = param;
238     args.fmtinfo          = &param->fmtinfo;
239     args.put.out          = args_outbuf;
240     args.put.__allocated_out = NULL;
241     args.put.outl         = (int) sizeof(args_outbuf);
242     args.is_ptr           = (flags & TYPFLAG_POINTER) ? 1 : 0;
243     args.format           = BINARYFMT;
244     args.put.expandBuffer = argsExpandBuffer;
245     args.typpos           = typpos;
246     args.typhandler       = h;
247     args.errorf           = pqt_argserrorf;
248     args.super            = pqt_argssuper;
249     *args.put.out         = 0;
250 
251     if (flags & TYPFLAG_ARRAY)
252       n = pqt_put_array(&args);
253     else
254       n = h->typput(&args);
255 
256     if (n == -1)
257     {
258       if (args.put.__allocated_out && args.put.__allocated_out != args_outbuf)
259         free(args.put.__allocated_out);
260       param->vcnt = save_vcnt;
261       return FALSE;
262     }
263 
264     if (args.put.out == NULL)
265     {
266       args.format = BINARYFMT;
267       n = -1;
268     }
269 
270     n = pqt_putparam(param, args.put.out, n, flags, args.format,
271       (flags & TYPFLAG_ARRAY) ? h->typoid_array : h->typoid);
272 
273     if (args.put.__allocated_out && args.put.__allocated_out != args_outbuf)
274       free(args.put.__allocated_out);
275 
276     if (!n)
277     {
278       param->vcnt = save_vcnt;
279       return FALSE;
280     }
281   }
282 
283   if (stmtBuf)
284     stmtBuf[stmtPos] = 0;
285 
286   return TRUE;
287 }
288 
289 
290 /* ----------------------------
291  * Helper functions
292  */
293 
294 int
295 pqt_putparam(PGparam *param, const void *data, int datal,
296   int flags, int format, Oid typoid)
297 {
298   PGvalue *v;
299 
300   if (!param)
301     return FALSE;
302 
303   if (!data)
304     datal = -1;
305 
306   /* need to grow param vals array */
307   if (param->vcnt == param->vmax)
308   {
309     PGvalue *vals;
310     int vmax = param->vmax ? (param->vmax * 3) / 2 : 16;
311 
312     vals = (PGvalue *) pqt_realloc(param->vals, sizeof(PGvalue) * vmax);
313     if (!vals)
314     {
315       PQseterror(PQT_OUTOFMEMORY);
316       return FALSE;
317     }
318 
319     /* zero out the new array elements */
320     memset(vals + param->vcnt, 0, (vmax - param->vcnt) * sizeof(PGvalue));
321     param->vmax = vmax;
322     param->vals = vals;
323   }
324 
325   /* grab next param value */
326   v = &param->vals[param->vcnt];
327 
328   if (datal == -1)
329   {
330     v->data = NULL;
331   }
332   /* wants to put a direct pointer */
333   else if (flags & TYPFLAG_POINTER)
334   {
335     v->data = (char *) data;
336   }
337   else
338   {
339     /* need more mem for param value ptr */
340     if (v->ptrl < datal)
341     {
342       char *ptr = (char *) pqt_realloc(v->ptr, datal);
343 
344       if (!ptr)
345       {
346         PQseterror(PQT_OUTOFMEMORY);
347         return FALSE;
348       }
349 
350       v->ptrl = datal;
351       v->ptr = ptr;
352     }
353 
354     memcpy(v->ptr, data, datal);
355     v->data = v->ptr;
356   }
357 
358   v->datal  = datal;
359   v->format = format;
360   v->oid    = typoid;
361   param->vcnt++;
362   return TRUE;
363 }
364 
365 /* returns 0 on success and -1 on error */
366 static int
367 argsExpandBuffer(PGtypeArgs *args, int new_len)
368 {
369   char *new_out;
370 
371   if (new_len <= args->put.outl)
372     return 0;
373 
374   if (!args->put.__allocated_out)
375   {
376     new_out = (char *) malloc(new_len);
377     if (!new_out)
378       return args->errorf(args, PQT_OUTOFMEMORY);
379 
380     /* fully copy existing stack buffer, we don't know what data
381      * the user has already written and may want to keep.
382      */
383     memcpy(new_out, args->put.out, args->put.outl);
384   }
385   else
386   {
387     new_out = (char *) realloc(args->put.__allocated_out, new_len);
388     if (!new_out)
389       return args->errorf(args, PQT_OUTOFMEMORY);
390   }
391 
392   args->put.out = args->put.__allocated_out = new_out;
393   args->put.outl = new_len;
394   return 0;
395 }