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, ¶m->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 = ¶m->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 = ¶m->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 } |